aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--client/.sass-lint.yml30
-rw-r--r--client/.stylelintrc.json29
-rw-r--r--client/e2e/src/po/video-watch.po.ts2
-rw-r--r--client/package.json7
-rw-r--r--client/src/app/+about/about-follows/about-follows.component.html2
-rw-r--r--client/src/app/+about/about-follows/about-follows.component.scss4
-rw-r--r--client/src/app/+about/about-instance/about-instance.component.scss6
-rw-r--r--client/src/app/+about/about-peertube/about-peertube.component.scss3
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.html7
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.scss14
-rw-r--r--client/src/app/+accounts/accounts.component.html2
-rw-r--r--client/src/app/+accounts/accounts.component.scss6
-rw-r--r--client/src/app/+accounts/accounts.module.ts4
-rw-r--r--client/src/app/+admin/admin.module.ts6
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss5
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html4
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html14
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.scss11
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts6
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html12
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.scss11
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts6
-rw-r--r--client/src/app/+admin/follows/follows.component.scss2
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss3
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts6
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-list.component.html2
-rw-r--r--client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html16
-rw-r--r--client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss2
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.html25
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss17
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts48
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html27
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss20
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts27
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html14
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss2
-rw-r--r--client/src/app/+admin/plugins/plugin-search/plugin-search.component.html11
-rw-r--r--client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss3
-rw-r--r--client/src/app/+admin/plugins/plugins.component.scss28
-rw-r--r--client/src/app/+admin/plugins/plugins.component.ts3
-rw-r--r--client/src/app/+admin/plugins/shared/plugin-list.component.scss16
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.scss2
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.ts6
-rw-r--r--client/src/app/+admin/system/logs/logs.component.scss4
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.scss3
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.scss2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html27
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss24
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts25
-rw-r--r--client/src/app/+login/login.component.scss19
-rw-r--r--client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html2
-rw-r--r--client/src/app/+my-account/my-account-applications/my-account-applications.component.scss2
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss3
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss2
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss2
-rw-r--r--client/src/app/+my-account/my-account.component.scss6
-rw-r--r--client/src/app/+my-account/my-account.module.ts8
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss3
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.html21
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss99
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts46
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.html32
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.scss4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.ts75
-rw-r--r--client/src/app/+my-library/my-library.component.scss6
-rw-r--r--client/src/app/+my-library/my-library.module.ts4
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.html2
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.scss8
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.ts6
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html14
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss20
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts35
-rw-r--r--client/src/app/+my-library/my-video-imports/my-video-imports.component.scss2
-rw-r--r--client/src/app/+my-library/my-video-imports/my-video-imports.component.ts2
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss13
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html7
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts45
-rw-r--r--client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss2
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.html9
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.scss4
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.ts35
-rw-r--r--client/src/app/+search/search-filters.component.scss2
-rw-r--r--client/src/app/+search/search.component.html11
-rw-r--r--client/src/app/+search/search.component.scss18
-rw-r--r--client/src/app/+search/search.component.ts26
-rw-r--r--client/src/app/+search/search.module.ts2
-rw-r--r--client/src/app/+signup/+register/register.component.scss2
-rw-r--r--client/src/app/+signup/shared/signup-success.component.scss22
-rw-r--r--client/src/app/+video-channels/video-channels.component.html20
-rw-r--r--client/src/app/+video-channels/video-channels.component.scss22
-rw-r--r--client/src/app/+video-channels/video-channels.module.ts4
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss4
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.scss6
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-send.scss6
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.scss2
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss9
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.html8
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.scss17
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.ts4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.scss5
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html4
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss3
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.html30
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.scss56
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.ts1
-rw-r--r--client/src/app/+videos/+video-watch/video-watch-playlist.component.scss2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.scss23
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.module.ts4
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.html2
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.scss15
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.ts4
-rw-r--r--client/src/app/+videos/video-list/trending/video-trending-header.component.scss2
-rw-r--r--client/src/app/+videos/videos.module.ts4
-rw-r--r--client/src/app/app.component.scss2
-rw-r--r--client/src/app/app.module.ts4
-rw-r--r--client/src/app/core/hotkeys/hotkeys.component.scss5
-rw-r--r--client/src/app/core/rest/rest-table.ts81
-rw-r--r--client/src/app/core/rest/rest.service.ts12
-rw-r--r--client/src/app/core/users/user.service.ts8
-rw-r--r--client/src/app/core/wrappers/screen.service.ts6
-rw-r--r--client/src/app/header/search-typeahead.component.scss13
-rw-r--r--client/src/app/header/suggestion.component.scss8
-rw-r--r--client/src/app/menu/language-chooser.component.scss4
-rw-r--r--client/src/app/menu/menu.component.html2
-rw-r--r--client/src/app/menu/menu.component.scss35
-rw-r--r--client/src/app/menu/notification.component.scss25
-rw-r--r--client/src/app/modal/welcome-modal.component.scss2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.html16
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.ts2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.html32
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts42
-rw-r--r--client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts4
-rw-r--r--client/src/app/shared/shared-account-avatar/account-avatar.component.html15
-rw-r--r--client/src/app/shared/shared-account-avatar/account-avatar.component.scss22
-rw-r--r--client/src/app/shared/shared-account-avatar/account-avatar.component.ts39
-rw-r--r--client/src/app/shared/shared-account-avatar/index.ts2
-rw-r--r--client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts23
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html (renamed from client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html)3
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss (renamed from client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss)10
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts (renamed from client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts)12
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html (renamed from client/src/app/shared/shared-actor-image/actor-banner-edit.component.html)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss (renamed from client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts (renamed from client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss (renamed from client/src/app/shared/shared-actor-image/actor-image-edit.scss)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/index.ts1
-rw-r--r--client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts31
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.html19
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.scss101
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts111
-rw-r--r--client/src/app/shared/shared-actor-image/shared-actor-image.module.ts14
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.html24
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.scss10
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.ts116
-rw-r--r--client/src/app/shared/shared-forms/index.ts10
-rw-r--r--client/src/app/shared/shared-forms/input-switch.component.scss8
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.scss25
-rw-r--r--client/src/app/shared/shared-forms/peertube-checkbox.component.scss6
-rw-r--r--client/src/app/shared/shared-forms/preview-upload.component.scss2
-rw-r--r--client/src/app/shared/shared-forms/select/select-shared.component.scss6
-rw-r--r--client/src/app/shared/shared-forms/shared-form.module.ts9
-rw-r--r--client/src/app/shared/shared-forms/timestamp-input.component.scss3
-rw-r--r--client/src/app/shared/shared-instance/instance-about-accordion.component.scss4
-rw-r--r--client/src/app/shared/shared-instance/instance-features-table.component.scss2
-rw-r--r--client/src/app/shared/shared-main/account/account.model.ts11
-rw-r--r--client/src/app/shared/shared-main/account/actor.model.ts1
-rw-r--r--client/src/app/shared/shared-main/buttons/action-dropdown.component.scss13
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.scss22
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.ts6
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.html5
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.ts1
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.html5
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.ts2
-rw-r--r--client/src/app/shared/shared-main/date/date-toggle.component.scss2
-rw-r--r--client/src/app/shared/shared-main/feeds/feed.component.scss6
-rw-r--r--client/src/app/shared/shared-main/loaders/loader.component.scss2
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.scss11
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.html8
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.scss6
-rw-r--r--client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss4
-rw-r--r--client/src/app/shared/shared-main/users/user-notification.model.ts4
-rw-r--r--client/src/app/shared/shared-main/users/user-notifications.component.scss11
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.scss3
-rw-r--r--client/src/app/shared/shared-main/video-channel/video-channel.model.ts5
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts3
-rw-r--r--client/src/app/shared/shared-main/video/video.service.ts12
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.html11
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.scss11
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.ts4
-rw-r--r--client/src/app/shared/shared-moderation/moderation.scss39
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.html14
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.scss21
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.ts6
-rw-r--r--client/src/app/shared/shared-moderation/shared-moderation.module.ts4
-rw-r--r--client/src/app/shared/shared-moderation/video-block.component.scss2
-rw-r--r--client/src/app/shared/shared-search/advanced-search.model.ts6
-rw-r--r--client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss10
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.html4
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.ts69
-rw-r--r--client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss2
-rw-r--r--client/src/app/shared/shared-user-subscription/subscribe-button.component.scss14
-rw-r--r--client/src/app/shared/shared-video-comment/video-comment.service.ts8
-rw-r--r--client/src/app/shared/shared-video-miniature/abstract-video-list.scss17
-rw-r--r--client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.scss4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.html13
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.scss11
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.ts11
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.html4
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts3
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss12
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.model.ts4
-rw-r--r--client/src/assets/player/images/info.svg1
-rw-r--r--client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts30
-rw-r--r--client/src/assets/player/peertube-player-local-storage.ts1
-rw-r--r--client/src/assets/player/peertube-player-manager.ts15
-rw-r--r--client/src/assets/player/peertube-videojs-typings.ts7
-rw-r--r--client/src/assets/player/stats/stats-card.ts273
-rw-r--r--client/src/assets/player/stats/stats-plugin.ts31
-rw-r--r--client/src/assets/player/videojs-components/settings-menu-button.ts13
-rw-r--r--client/src/assets/player/webtorrent/webtorrent-plugin.ts3
-rw-r--r--client/src/sass/application.scss57
-rw-r--r--client/src/sass/bootstrap.scss40
-rw-r--r--client/src/sass/include/_actor.scss14
-rw-r--r--client/src/sass/include/_bootstrap.scss2
-rw-r--r--client/src/sass/include/_fonts.scss4
-rw-r--r--client/src/sass/include/_miniature.scss17
-rw-r--r--client/src/sass/include/_mixins.scss175
-rw-r--r--client/src/sass/include/_variables.scss6
-rw-r--r--client/src/sass/ng-select.scss9
-rw-r--r--client/src/sass/player/context-menu.scss12
-rw-r--r--client/src/sass/player/index.scss5
-rw-r--r--client/src/sass/player/mobile.scss2
-rw-r--r--client/src/sass/player/peertube-skin.scss48
-rw-r--r--client/src/sass/player/playlist.scss12
-rw-r--r--client/src/sass/player/settings-menu.scss2
-rw-r--r--client/src/sass/player/spinner.scss2
-rw-r--r--client/src/sass/player/stats.scss41
-rw-r--r--client/src/sass/player/upnext.scss14
-rw-r--r--client/src/sass/primeng-custom.scss11
-rw-r--r--client/src/standalone/videos/embed.scss25
-rw-r--r--client/src/standalone/videos/test-embed.scss22
-rw-r--r--client/yarn.lock1394
-rw-r--r--package.json59
-rwxr-xr-xscripts/clean/client/index.sh (renamed from scripts/clean/client/dist.sh)0
-rwxr-xr-xscripts/clean/server/dist.sh5
-rw-r--r--scripts/danger/clean/cleaner.ts34
-rwxr-xr-xscripts/danger/clean/dev.sh10
-rwxr-xr-xscripts/danger/clean/modules.sh9
-rwxr-xr-xscripts/danger/clean/prod.sh10
-rwxr-xr-xscripts/help.sh5
-rwxr-xr-xscripts/i18n/create-custom-files.ts17
-rwxr-xr-xscripts/play.sh15
-rwxr-xr-xscripts/prune-storage.ts2
-rw-r--r--server/controllers/api/accounts.ts27
-rw-r--r--server/controllers/api/users/me.ts3
-rw-r--r--server/controllers/api/users/my-subscriptions.ts23
-rw-r--r--server/controllers/api/video-channel.ts23
-rw-r--r--server/controllers/api/videos/index.ts25
-rw-r--r--server/helpers/custom-validators/search.ts4
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts7
-rw-r--r--server/lib/video.ts8
-rw-r--r--server/middlewares/validators/videos/videos.ts8
-rw-r--r--server/models/video/video-query-builder.ts12
-rw-r--r--server/models/video/video.ts52
-rw-r--r--server/tests/api/live/live.ts65
-rw-r--r--server/tests/api/search/search-videos.ts49
-rw-r--r--server/tests/api/videos/single-server.ts4
-rw-r--r--server/tests/api/videos/video-transcoder.ts7
-rw-r--r--shared/extra-utils/videos/videos.ts16
-rw-r--r--shared/models/search/boolean-both-query.model.ts1
-rw-r--r--shared/models/search/index.ts3
-rw-r--r--shared/models/search/nsfw-query.model.ts1
-rw-r--r--shared/models/search/videos-common-query.model.ts28
-rw-r--r--shared/models/search/videos-search-query.model.ts22
-rw-r--r--support/doc/api/openapi.yaml241
-rw-r--r--support/doc/production.md2
-rw-r--r--yarn.lock33
285 files changed, 3411 insertions, 2546 deletions
diff --git a/README.md b/README.md
index 377794a0a..f5fb6acea 100644
--- a/README.md
+++ b/README.md
@@ -142,14 +142,14 @@ Feel free to reach out if you have any questions or ideas! :speech_balloon:
142:package: Create your own instance 142:package: Create your own instance
143---------------------------------------------------------------- 143----------------------------------------------------------------
144 144
145See the [production guide](https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/production.md), which is the recommended way to install or upgrade PeerTube. For hardware requirements, see [Should I have a big server to run PeerTube?](https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md#should-i-have-a-big-server-to-run-peertube) in the FAQ. 145See the [production guide](https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/production.md), which is the recommended way to install or upgrade PeerTube. For hardware requirements, see [Should I have a big server to run PeerTube?](https://joinpeertube.org/faq#should-i-have-a-big-server-to-run-peertube) in the FAQ.
146 146
147See the [community packages](https://docs.joinpeertube.org/install-unofficial), which cover various platforms (including [YunoHost](https://install-app.yunohost.org/?app=peertube) and [Docker](https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/docker.md)). 147See the [community packages](https://docs.joinpeertube.org/install-unofficial), which cover various platforms (including [YunoHost](https://install-app.yunohost.org/?app=peertube) and [Docker](https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/docker.md)).
148 148
149:book: Documentation 149:book: Documentation
150---------------------------------------------------------------- 150----------------------------------------------------------------
151 151
152If you have a question, please try to find the answer in the [FAQ](https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md) first. 152If you have a question, please try to find the answer in the [FAQ](https://joinpeertube.org/faq) first.
153 153
154### User documentation 154### User documentation
155 155
diff --git a/client/.sass-lint.yml b/client/.sass-lint.yml
deleted file mode 100644
index a6e949cb2..000000000
--- a/client/.sass-lint.yml
+++ /dev/null
@@ -1,30 +0,0 @@
1files:
2 include:
3 - "src/app/**/*.scss"
4 - "src/assets/**/*.scss"
5 - "src/sass/**/*.scss"
6 - "src/standalone/**/*.scss"
7syntax:
8 include:
9 - scss
10 - sass
11rules:
12 property-sort-order: 0
13 attribute-quotes: 0
14 border-zero: 0
15 no-color-keywords: 0
16 no-color-literals: 0
17 no-css-comments: 0
18 no-important: 0
19 no-trailing-zero: 1
20 space-after-bang: 1
21 space-before-bang: 1
22 space-after-colon: 1
23 space-before-colon: 1
24 clean-import-paths: 0
25 hex-length: 1
26 hex-notation: 0
27 nesting-depth:
28 - 1
29 - max-depth: 4
30 indentation: 2
diff --git a/client/.stylelintrc.json b/client/.stylelintrc.json
new file mode 100644
index 000000000..25f0b1002
--- /dev/null
+++ b/client/.stylelintrc.json
@@ -0,0 +1,29 @@
1{
2 "extends": "stylelint-config-sass-guidelines",
3 "rules": {
4 "scss/at-import-no-partial-leading-underscore": null,
5 "color-hex-case": null,
6 "color-hex-length": null,
7 "order/properties-alphabetical-order": null,
8 "selector-pseudo-element-no-unknown": [
9 true,
10 {
11 "ignorePseudoElements": [ "ng-deep" ]
12 }
13 ],
14 "max-nesting-depth": [
15 8,
16 {
17 "ignore": [ "blockless-at-rules", "pseudo-classes" ]
18 }
19 ],
20 "selector-max-compound-selectors": 9,
21 "selector-no-qualifying-type": null,
22 "scss/at-extend-no-missing-placeholder": null,
23 "number-leading-zero": null,
24 "rule-empty-line-before": null,
25 "selector-max-id": null,
26 "scss/at-function-pattern": null,
27 "function-parentheses-space-inside": "never-single-line"
28 }
29}
diff --git a/client/e2e/src/po/video-watch.po.ts b/client/e2e/src/po/video-watch.po.ts
index fb9c3a000..b41fe0882 100644
--- a/client/e2e/src/po/video-watch.po.ts
+++ b/client/e2e/src/po/video-watch.po.ts
@@ -86,7 +86,7 @@ export class VideoWatchPage {
86 const dropdown = element(by.css('my-video-actions-dropdown .action-button')) 86 const dropdown = element(by.css('my-video-actions-dropdown .action-button'))
87 await dropdown.click() 87 await dropdown.click()
88 88
89 const items: ElementFinder[] = await element.all(by.css('my-video-actions-dropdown .dropdown-menu .dropdown-item')) 89 const items: ElementFinder[] = await element.all(by.css('.dropdown-menu.show .dropdown-item'))
90 90
91 for (const item of items) { 91 for (const item of items) {
92 const href = await item.getAttribute('href') 92 const href = await item.getAttribute('href')
diff --git a/client/package.json b/client/package.json
index 8a344c1af..140fc3095 100644
--- a/client/package.json
+++ b/client/package.json
@@ -15,14 +15,14 @@
15 "scripts": { 15 "scripts": {
16 "lint": "npm run lint-ts && npm run lint-scss", 16 "lint": "npm run lint-ts && npm run lint-scss",
17 "lint-ts": "tslint --project ./tsconfig.app.json -c ./tslint.json 'src/app/**/*.ts' 'src/standalone/**/*.ts'", 17 "lint-ts": "tslint --project ./tsconfig.app.json -c ./tslint.json 'src/app/**/*.ts' 'src/standalone/**/*.ts'",
18 "lint-scss": "sass-lint -c .sass-lint.yml", 18 "lint-scss": "stylelint 'src/**/*.scss'",
19 "webpack": "webpack", 19 "webpack": "webpack",
20 "tslint": "tslint", 20 "tslint": "tslint",
21 "ng": "ng", 21 "ng": "ng",
22 "webpack-bundle-analyzer": "webpack-bundle-analyzer", 22 "webpack-bundle-analyzer": "webpack-bundle-analyzer",
23 "webdriver-manager": "webdriver-manager", 23 "webdriver-manager": "webdriver-manager",
24 "ngx-extractor": "ngx-extractor", 24 "ngx-extractor": "ngx-extractor",
25 "sass-lint": "sass-lint" 25 "stylelint": "stylelint"
26 }, 26 },
27 "typings": "*.d.ts", 27 "typings": "*.d.ts",
28 "resolutions": { 28 "resolutions": {
@@ -106,13 +106,14 @@
106 "rxjs": "^6.5.2", 106 "rxjs": "^6.5.2",
107 "sanitize-html": "^2.1.2", 107 "sanitize-html": "^2.1.2",
108 "sass": "^1.29.0", 108 "sass": "^1.29.0",
109 "sass-lint": "^1.13.1",
110 "sass-loader": "^10", 109 "sass-loader": "^10",
111 "sass-resources-loader": "^2.0.0", 110 "sass-resources-loader": "^2.0.0",
112 "sha.js": "^2.4.11", 111 "sha.js": "^2.4.11",
113 "socket.io-client": "^4.0.1", 112 "socket.io-client": "^4.0.1",
114 "stream-browserify": "^3.0.0", 113 "stream-browserify": "^3.0.0",
115 "stream-http": "^3.0.0", 114 "stream-http": "^3.0.0",
115 "stylelint": "^13.13.0",
116 "stylelint-config-sass-guidelines": "^8.0.0",
116 "terser-webpack-plugin": "^4", 117 "terser-webpack-plugin": "^4",
117 "ts-loader": "^8.0.14", 118 "ts-loader": "^8.0.14",
118 "tslib": "^2.0.0", 119 "tslib": "^2.0.0",
diff --git a/client/src/app/+about/about-follows/about-follows.component.html b/client/src/app/+about/about-follows/about-follows.component.html
index e9139b503..f81465f88 100644
--- a/client/src/app/+about/about-follows/about-follows.component.html
+++ b/client/src/app/+about/about-follows/about-follows.component.html
@@ -21,7 +21,7 @@
21 {{ following }} 21 {{ following }}
22 </a> 22 </a>
23 23
24 <button i18n class="showMore" *ngIf="!loadedAllFollowings && canLoadMoreFollowings()" (click)="loadAllFollowings()">Show full list</button> 24 <button i18n class="show-more" *ngIf="!loadedAllFollowings && canLoadMoreFollowings()" (click)="loadAllFollowings()">Show full list</button>
25 </div> 25 </div>
26 26
27</div> 27</div>
diff --git a/client/src/app/+about/about-follows/about-follows.component.scss b/client/src/app/+about/about-follows/about-follows.component.scss
index a393c9d92..83241e727 100644
--- a/client/src/app/+about/about-follows/about-follows.component.scss
+++ b/client/src/app/+about/about-follows/about-follows.component.scss
@@ -17,9 +17,9 @@ a {
17 justify-content: flex-start; 17 justify-content: flex-start;
18} 18}
19 19
20.showMore { 20.show-more {
21 @include peertube-button-link; 21 @include peertube-button-link;
22 @include grey-button; 22 @include grey-button;
23 23
24 margin-top: 1%; 24 margin-top: 1%;
25} 25}
diff --git a/client/src/app/+about/about-instance/about-instance.component.scss b/client/src/app/+about/about-instance/about-instance.component.scss
index 7158a3a79..9886bdfef 100644
--- a/client/src/app/+about/about-instance/about-instance.component.scss
+++ b/client/src/app/+about/about-instance/about-instance.component.scss
@@ -63,7 +63,8 @@
63 63
64 position: relative; 64 position: relative;
65 65
66 &:hover, &:active { 66 &:hover,
67 &:active {
67 &::after { 68 &::after {
68 content: '#'; 69 content: '#';
69 display: inline-block; 70 display: inline-block;
@@ -71,7 +72,8 @@
71 } 72 }
72 } 73 }
73 74
74 .middle-title, .section-title { 75 .middle-title,
76 .section-title {
75 display: inline-block; 77 display: inline-block;
76 } 78 }
77 79
diff --git a/client/src/app/+about/about-peertube/about-peertube.component.scss b/client/src/app/+about/about-peertube/about-peertube.component.scss
index e67252410..e5d2bc5b8 100644
--- a/client/src/app/+about/about-peertube/about-peertube.component.scss
+++ b/client/src/app/+about/about-peertube/about-peertube.component.scss
@@ -45,7 +45,8 @@
45.p2p-privacy, 45.p2p-privacy,
46my-about-peertube-contributors { 46my-about-peertube-contributors {
47 ::ng-deep { 47 ::ng-deep {
48 p, li { 48 p,
49 li {
49 font-size: 15px; 50 font-size: 15px;
50 } 51 }
51 } 52 }
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
index 19a4b3c9c..922608127 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
@@ -8,9 +8,10 @@
8 <div class="channel" *ngFor="let videoChannel of videoChannels"> 8 <div class="channel" *ngFor="let videoChannel of videoChannels">
9 9
10 <div class="channel-avatar-row"> 10 <div class="channel-avatar-row">
11 <a class="avatar-link" [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> 11 <my-actor-avatar
12 <img [src]="videoChannel.avatarUrl" alt="Avatar" /> 12 [channel]="videoChannel" [internalHref]="getVideoChannelLink(videoChannel)"
13 </a> 13 i18n-title title="See this video channel"
14 ></my-actor-avatar>
14 15
15 <h2> 16 <h2>
16 <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> 17 <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel">
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss
index 7e88802f3..f9d097644 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss
@@ -27,14 +27,12 @@
27 grid-template-columns: auto auto 1fr; 27 grid-template-columns: auto auto 1fr;
28 grid-template-rows: auto 1fr; 28 grid-template-rows: auto 1fr;
29 29
30 .avatar-link { 30 my-actor-avatar {
31 @include actor-avatar-size(75px);
32
31 grid-column: 1; 33 grid-column: 1;
32 grid-row: 1 / 3; 34 grid-row: 1 / 3;
33 margin-right: 30px; 35 margin-right: 15px;
34 }
35
36 img {
37 @include channel-avatar(75px);
38 } 36 }
39 37
40 a { 38 a {
@@ -67,13 +65,13 @@
67 } 65 }
68 66
69 .description-html { 67 .description-html {
68 @include fade-text(30px, pvar(--channelBackgroundColor));
69
70 grid-column: 2 / 4; 70 grid-column: 2 / 4;
71 grid-row: 2; 71 grid-row: 2;
72 72
73 max-height: 80px; 73 max-height: 80px;
74 font-size: 16px; 74 font-size: 16px;
75
76 @include fade-text(30px, pvar(--channelBackgroundColor));
77 } 75 }
78} 76}
79 77
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index ea7a317eb..350c77f1e 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -2,7 +2,7 @@
2 <div class="account-info"> 2 <div class="account-info">
3 3
4 <div class="account-avatar-row"> 4 <div class="account-avatar-row">
5 <my-account-avatar [account]="account" size="120"></my-account-avatar> 5 <my-actor-avatar class="main-avatar" [account]="account"></my-actor-avatar>
6 6
7 <div> 7 <div>
8 <div class="section-label" i18n>PEERTUBE ACCOUNT</div> 8 <div class="section-label" i18n>PEERTUBE ACCOUNT</div>
diff --git a/client/src/app/+accounts/accounts.component.scss b/client/src/app/+accounts/accounts.component.scss
index 56927dea6..2e99fe8a5 100644
--- a/client/src/app/+accounts/accounts.component.scss
+++ b/client/src/app/+accounts/accounts.component.scss
@@ -40,7 +40,7 @@ my-user-moderation-dropdown,
40} 40}
41 41
42.copy-button { 42.copy-button {
43 border: none; 43 border: 0;
44} 44}
45 45
46.account-info { 46.account-info {
@@ -104,9 +104,9 @@ my-user-moderation-dropdown,
104 } 104 }
105 105
106 .description:not(.expanded) { 106 .description:not(.expanded) {
107 max-height: 70px;
108
109 @include fade-text(30px, pvar(--submenuBackgroundColor)); 107 @include fade-text(30px, pvar(--submenuBackgroundColor));
108
109 max-height: 70px;
110 } 110 }
111 111
112 .show-more { 112 .show-more {
diff --git a/client/src/app/+accounts/accounts.module.ts b/client/src/app/+accounts/accounts.module.ts
index 22cdd0642..1bafc5141 100644
--- a/client/src/app/+accounts/accounts.module.ts
+++ b/client/src/app/+accounts/accounts.module.ts
@@ -10,7 +10,7 @@ import { AccountVideoChannelsComponent } from './account-video-channels/account-
10import { AccountVideosComponent } from './account-videos/account-videos.component' 10import { AccountVideosComponent } from './account-videos/account-videos.component'
11import { AccountsRoutingModule } from './accounts-routing.module' 11import { AccountsRoutingModule } from './accounts-routing.module'
12import { AccountsComponent } from './accounts.component' 12import { AccountsComponent } from './accounts.component'
13import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' 13import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
14 14
15@NgModule({ 15@NgModule({
16 imports: [ 16 imports: [
@@ -22,7 +22,7 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
22 SharedModerationModule, 22 SharedModerationModule,
23 SharedVideoMiniatureModule, 23 SharedVideoMiniatureModule,
24 SharedGlobalIconModule, 24 SharedGlobalIconModule,
25 SharedAccountAvatarModule 25 SharedActorImageModule
26 ], 26 ],
27 27
28 declarations: [ 28 declarations: [
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index 8d1c3eadb..45366f9ec 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -3,12 +3,13 @@ import { SelectButtonModule } from 'primeng/selectbutton'
3import { TableModule } from 'primeng/table' 3import { TableModule } from 'primeng/table'
4import { NgModule } from '@angular/core' 4import { NgModule } from '@angular/core'
5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' 5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
6import { SharedActorImageModule } from '@app/shared/shared-actor-image' 6import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit'
7import { SharedFormModule } from '@app/shared/shared-forms' 7import { SharedFormModule } from '@app/shared/shared-forms'
8import { SharedGlobalIconModule } from '@app/shared/shared-icons' 8import { SharedGlobalIconModule } from '@app/shared/shared-icons'
9import { SharedMainModule } from '@app/shared/shared-main' 9import { SharedMainModule } from '@app/shared/shared-main'
10import { SharedModerationModule } from '@app/shared/shared-moderation' 10import { SharedModerationModule } from '@app/shared/shared-moderation'
11import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' 11import { SharedVideoCommentModule } from '@app/shared/shared-video-comment'
12import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
12import { AdminRoutingModule } from './admin-routing.module' 13import { AdminRoutingModule } from './admin-routing.module'
13import { AdminComponent } from './admin.component' 14import { AdminComponent } from './admin.component'
14import { 15import {
@@ -39,7 +40,6 @@ import { JobService, LogsComponent, LogsService, SystemComponent } from './syste
39import { DebugComponent, DebugService } from './system/debug' 40import { DebugComponent, DebugService } from './system/debug'
40import { JobsComponent } from './system/jobs/jobs.component' 41import { JobsComponent } from './system/jobs/jobs.component'
41import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users' 42import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users'
42import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
43 43
44@NgModule({ 44@NgModule({
45 imports: [ 45 imports: [
@@ -51,8 +51,8 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
51 SharedGlobalIconModule, 51 SharedGlobalIconModule,
52 SharedAbuseListModule, 52 SharedAbuseListModule,
53 SharedVideoCommentModule, 53 SharedVideoCommentModule,
54 SharedAccountAvatarModule,
55 SharedActorImageModule, 54 SharedActorImageModule,
55 SharedActorImageEditModule,
56 56
57 TableModule, 57 TableModule,
58 SelectButtonModule, 58 SelectButtonModule,
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
index c12465d45..cc2a98a17 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
@@ -57,7 +57,7 @@ input[type=submit] {
57 display: flex; 57 display: flex;
58 margin-left: auto; 58 margin-left: auto;
59 59
60 & + .form-error { 60 + .form-error {
61 display: inline; 61 display: inline;
62 margin-left: 5px; 62 margin-left: 5px;
63 } 63 }
@@ -84,7 +84,8 @@ textarea {
84} 84}
85 85
86.disabled-checkbox-extra { 86.disabled-checkbox-extra {
87 &, ::ng-deep label { 87 &,
88 ::ng-deep label {
88 opacity: .5; 89 opacity: .5;
89 pointer-events: none; 90 pointer-events: none;
90 } 91 }
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
index 35b42e742..cbff26e5a 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
@@ -104,7 +104,7 @@
104 <my-help> 104 <my-help>
105 <ng-template ptTemplate="customHtml"> 105 <ng-template ptTemplate="customHtml">
106 <ng-container i18n> 106 <ng-container i18n>
107 With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video. 107 With <strong>Hide</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
108 </ng-container> 108 </ng-container>
109 </ng-template> 109 </ng-template>
110 </my-help> 110 </my-help>
@@ -112,7 +112,7 @@
112 <div class="peertube-select-container"> 112 <div class="peertube-select-container">
113 <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy" class="form-control"> 113 <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy" class="form-control">
114 <option i18n value="undefined" disabled>Policy for sensitive videos</option> 114 <option i18n value="undefined" disabled>Policy for sensitive videos</option>
115 <option i18n value="do_not_list">Do not list</option> 115 <option i18n value="do_not_list">Hide</option>
116 <option i18n value="blur">Blur thumbnails</option> 116 <option i18n value="blur">Blur thumbnails</option>
117 <option i18n value="display">Display</option> 117 <option i18n value="display">Display</option>
118 </select> 118 </select>
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index 633de9677..c2e9a4df6 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -4,20 +4,16 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="followers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="followers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 8 [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers"
11> 12>
12 <ng-template pTemplate="caption"> 13 <ng-template pTemplate="caption">
13 <div class="caption"> 14 <div class="caption">
14 <div class="ml-auto has-feedback has-clear"> 15 <div class="ml-auto">
15 <input 16 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
16 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
17 (keyup)="onSearch($event)"
18 >
19 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
20 <span class="sr-only" i18n>Clear filters</span>
21 </div> 17 </div>
22 </div> 18 </div>
23 </ng-template> 19 </ng-template>
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.scss b/client/src/app/+admin/follows/followers-list/followers-list.component.scss
index f2d752eb5..35f38aae0 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.scss
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.scss
@@ -1,19 +1,12 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.caption {
5 justify-content: flex-end;
6
7 input {
8 @include peertube-input-text(250px);
9 }
10}
11
12a { 4a {
13 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
14 display: inline-block; 6 display: inline-block;
15 7
16 &, &:hover { 8 &,
9 &:hover {
17 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
18 } 11 }
19 12
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
index 904e3c338..4a312f6aa 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -59,7 +59,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
59 const handle = follow.follower.name + '@' + follow.follower.host 59 const handle = follow.follower.name + '@' + follow.follower.host
60 this.notifier.success($localize`${handle} rejected from instance followers`) 60 this.notifier.success($localize`${handle} rejected from instance followers`)
61 61
62 this.loadData() 62 this.reloadData()
63 }, 63 },
64 64
65 err => { 65 err => {
@@ -80,14 +80,14 @@ export class FollowersListComponent extends RestTable implements OnInit {
80 const handle = follow.follower.name + '@' + follow.follower.host 80 const handle = follow.follower.name + '@' + follow.follower.host
81 this.notifier.success($localize`${handle} removed from instance followers`) 81 this.notifier.success($localize`${handle} removed from instance followers`)
82 82
83 this.loadData() 83 this.reloadData()
84 }, 84 },
85 85
86 err => this.notifier.error(err.message) 86 err => this.notifier.error(err.message)
87 ) 87 )
88 } 88 }
89 89
90 protected loadData () { 90 protected reloadData () {
91 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search }) 91 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
92 .subscribe( 92 .subscribe(
93 resultList => { 93 resultList => {
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index f4e6a60fe..e7c0c9088 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -4,8 +4,9 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="following" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="following" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts"
11> 12>
@@ -18,13 +19,8 @@
18 </a> 19 </a>
19 </div> 20 </div>
20 21
21 <div class="ml-auto has-feedback has-clear"> 22 <div class="ml-auto">
22 <input 23 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
23 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
24 (keyup)="onSearch($event)"
25 >
26 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
27 <span class="sr-only" i18n>Clear filters</span>
28 </div> 24 </div>
29 </div> 25 </div>
30 </ng-template> 26 </ng-template>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.scss b/client/src/app/+admin/follows/following-list/following-list.component.scss
index b108218b8..9474b0a23 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.scss
+++ b/client/src/app/+admin/follows/following-list/following-list.component.scss
@@ -5,7 +5,8 @@ a {
5 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
6 display: inline-block; 6 display: inline-block;
7 7
8 &, &:hover { 8 &,
9 &:hover {
9 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
10 } 11 }
11 12
@@ -15,14 +16,6 @@ a {
15 } 16 }
16} 17}
17 18
18.caption {
19 justify-content: flex-end;
20
21 input {
22 @include peertube-input-text(250px);
23 }
24}
25
26.follow-button { 19.follow-button {
27 @include create-button; 20 @include create-button;
28} 21}
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index f34490cc8..b63fe08c0 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -45,7 +45,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
45 this.followService.follow(hosts).subscribe( 45 this.followService.follow(hosts).subscribe(
46 () => { 46 () => {
47 this.notifier.success($localize`Follow request(s) sent!`) 47 this.notifier.success($localize`Follow request(s) sent!`)
48 this.loadData() 48 this.reloadData()
49 }, 49 },
50 50
51 err => this.notifier.error(err.message) 51 err => this.notifier.error(err.message)
@@ -62,14 +62,14 @@ export class FollowingListComponent extends RestTable implements OnInit {
62 this.followService.unfollow(follow).subscribe( 62 this.followService.unfollow(follow).subscribe(
63 () => { 63 () => {
64 this.notifier.success($localize`You are not following ${follow.following.host} anymore.`) 64 this.notifier.success($localize`You are not following ${follow.following.host} anymore.`)
65 this.loadData() 65 this.reloadData()
66 }, 66 },
67 67
68 err => this.notifier.error(err.message) 68 err => this.notifier.error(err.message)
69 ) 69 )
70 } 70 }
71 71
72 protected loadData () { 72 protected reloadData () {
73 this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search }) 73 this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search })
74 .subscribe( 74 .subscribe(
75 resultList => { 75 resultList => {
diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss
index 33ff17539..1ed0d925f 100644
--- a/client/src/app/+admin/follows/follows.component.scss
+++ b/client/src/app/+admin/follows/follows.component.scss
@@ -1,4 +1,4 @@
1@import "mixins"; 1@import 'mixins';
2 2
3.form-sub-title { 3.form-sub-title {
4 flex-grow: 0; 4 flex-grow: 0;
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss
index adcf2037e..30b9f2147 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss
@@ -5,7 +5,8 @@ a {
5 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
6 display: inline-block; 6 display: inline-block;
7 7
8 &, &:hover { 8 &,
9 &:hover {
9 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
10 } 11 }
11 12
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
index d6fd1a1ab..3cd65dd6e 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
@@ -78,7 +78,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
78 this.pagination.start = 0 78 this.pagination.start = 0
79 this.saveSelectLocalStorage() 79 this.saveSelectLocalStorage()
80 80
81 this.loadData() 81 this.reloadData()
82 } 82 }
83 83
84 getRedundancyStrategy (redundancy: VideoRedundancy) { 84 getRedundancyStrategy (redundancy: VideoRedundancy) {
@@ -145,7 +145,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
145 .subscribe( 145 .subscribe(
146 () => { 146 () => {
147 this.notifier.success($localize`Video redundancies removed!`) 147 this.notifier.success($localize`Video redundancies removed!`)
148 this.loadData() 148 this.reloadData()
149 }, 149 },
150 150
151 err => this.notifier.error(err.message) 151 err => this.notifier.error(err.message)
@@ -153,7 +153,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
153 153
154 } 154 }
155 155
156 protected loadData () { 156 protected reloadData () {
157 const options = { 157 const options = {
158 pagination: this.pagination, 158 pagination: this.pagination,
159 sort: this.sort, 159 sort: this.sort,
diff --git a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
index 9a6c124e1..a9e0931f8 100644
--- a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
@@ -3,4 +3,4 @@
3 <ng-container i18n>Reports</ng-container> 3 <ng-container i18n>Reports</ng-container>
4</h1> 4</h1>
5 5
6<my-abuse-list-table viewType="admin" baseRoute="/admin/moderation/abuses/list"></my-abuse-list-table> 6<my-abuse-list-table viewType="admin"></my-abuse-list-table>
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
index f5cf93adb..e3a3a8320 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
@@ -1,18 +1,14 @@
1<p-table 1<p-table
2 [value]="blockedAccounts" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="blockedAccounts" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 3 [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)"
4 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts" 6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts"
6> 7>
7 <ng-template pTemplate="caption"> 8 <ng-template pTemplate="caption">
8 <div class="caption"> 9 <div class="caption">
9 <div class="ml-auto has-feedback has-clear"> 10 <div class="ml-auto">
10 <input 11 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
12 (keyup)="onSearch($event)"
13 >
14 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
15 <span class="sr-only" i18n>Clear filters</span>
16 </div> 12 </div>
17 </div> 13 </div>
18 </ng-template> 14 </ng-template>
@@ -34,7 +30,7 @@
34 <td> 30 <td>
35 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 31 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
36 <div class="chip two-lines"> 32 <div class="chip two-lines">
37 <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar> 33 <my-actor-avatar [account]="accountBlock.blockedAccount"></my-actor-avatar>
38 <div> 34 <div>
39 {{ accountBlock.blockedAccount.displayName }} 35 {{ accountBlock.blockedAccount.displayName }}
40 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span> 36 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
index 6028b75ea..1d98e44d9 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
@@ -4,4 +4,4 @@
4.unblock-button { 4.unblock-button {
5 @include peertube-button; 5 @include peertube-button;
6 @include grey-button; 6 @include grey-button;
7} \ No newline at end of file 7}
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
index c7275de1b..d89c8f244 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
@@ -4,8 +4,9 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="blocklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="blocklist" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" 8 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos"
11 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 12 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -13,25 +14,7 @@
13 <ng-template pTemplate="caption"> 14 <ng-template pTemplate="caption">
14 <div class="caption"> 15 <div class="caption">
15 <div class="ml-auto"> 16 <div class="ml-auto">
16 <div class="input-group has-feedback has-clear"> 17 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
17 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
18 <div class="input-group-text" ngbDropdownToggle>
19 <span class="caret" aria-haspopup="menu" role="button"></span>
20 </div>
21
22 <div role="menu" ngbDropdownMenu>
23 <h6 class="dropdown-header" i18n>Advanced block filters</h6>
24 <a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:auto' }" class="dropdown-item" i18n>Automatic blocks</a>
25 <a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:manual' }" class="dropdown-item" i18n>Manual blocks</a>
26 </div>
27 </div>
28 <input
29 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
30 (keyup)="onSearch($event)"
31 >
32 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
33 <span class="sr-only" i18n>Clear filters</span>
34 </div>
35 </div> 18 </div>
36 </div> 19 </div>
37 </ng-template> 20 </ng-template>
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
index b67e33cc1..068aa2aee 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
@@ -5,23 +5,6 @@ my-global-icon {
5 height: 24px; 5 height: 24px;
6} 6}
7 7
8.input-group {
9 @include peertube-input-group(300px);
10
11 .dropdown-toggle::after {
12 margin-left: 0;
13 }
14}
15
16.caption {
17 justify-content: flex-end;
18
19 input {
20 @include peertube-input-text(250px);
21 flex-grow: 1;
22 }
23}
24
25.badge { 8.badge {
26 @include table-badge; 9 @include table-badge;
27} 10}
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index d6aca10e7..498d8321a 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -2,10 +2,11 @@ import { SortMeta } from 'primeng/api'
2import { switchMap } from 'rxjs/operators' 2import { switchMap } from 'rxjs/operators'
3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment' 4import { environment } from 'src/environments/environment'
5import { AfterViewInit, Component, OnInit } from '@angular/core' 5import { Component, OnInit } from '@angular/core'
6import { DomSanitizer } from '@angular/platform-browser' 6import { DomSanitizer } from '@angular/platform-browser'
7import { ActivatedRoute, Params, Router } from '@angular/router' 7import { ActivatedRoute, Router } from '@angular/router'
8import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' 8import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
9import { AdvancedInputFilter } from '@app/shared/shared-forms'
9import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
10import { VideoBlockService } from '@app/shared/shared-moderation' 11import { VideoBlockService } from '@app/shared/shared-moderation'
11import { VideoBlacklist, VideoBlacklistType } from '@shared/models' 12import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
@@ -15,7 +16,7 @@ import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
15 templateUrl: './video-block-list.component.html', 16 templateUrl: './video-block-list.component.html',
16 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ] 17 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ]
17}) 18})
18export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit { 19export class VideoBlockListComponent extends RestTable implements OnInit {
19 blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = [] 20 blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = []
20 totalRecords = 0 21 totalRecords = 0
21 sort: SortMeta = { field: 'createdAt', order: -1 } 22 sort: SortMeta = { field: 'createdAt', order: -1 }
@@ -24,6 +25,17 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
24 25
25 videoBlocklistActions: DropdownAction<VideoBlacklist>[][] = [] 26 videoBlocklistActions: DropdownAction<VideoBlacklist>[][] = []
26 27
28 inputFilters: AdvancedInputFilter[] = [
29 {
30 queryParams: { 'search': 'type:auto' },
31 label: $localize`Automatic blocks`
32 },
33 {
34 queryParams: { 'search': 'type:manual' },
35 label: $localize`Manual blocks`
36 }
37 ]
38
27 constructor ( 39 constructor (
28 protected route: ActivatedRoute, 40 protected route: ActivatedRoute,
29 protected router: Router, 41 protected router: Router,
@@ -52,7 +64,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
52 ).subscribe( 64 ).subscribe(
53 () => { 65 () => {
54 this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`) 66 this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`)
55 this.loadData() 67 this.reloadData()
56 }, 68 },
57 69
58 err => this.notifier.error(err.message) 70 err => this.notifier.error(err.message)
@@ -104,31 +116,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
104 }) 116 })
105 117
106 this.initialize() 118 this.initialize()
107 this.listenToSearchChange()
108 }
109
110 ngAfterViewInit () {
111 if (this.search) this.setTableFilter(this.search, false)
112 }
113
114 /* Table filter functions */
115 onBlockSearch (event: Event) {
116 this.onSearch(event)
117 this.setQueryParams((event.target as HTMLInputElement).value)
118 }
119
120 setQueryParams (search: string) {
121 const queryParams: Params = {}
122 if (search) Object.assign(queryParams, { search })
123 this.router.navigate([ '/admin/moderation/video-blocks/list' ], { queryParams })
124 }
125
126 resetTableFilter () {
127 this.setTableFilter('')
128 this.setQueryParams('')
129 this.resetSearch()
130 } 119 }
131 /* END Table filter functions */
132 120
133 getIdentifier () { 121 getIdentifier () {
134 return 'VideoBlockListComponent' 122 return 'VideoBlockListComponent'
@@ -151,7 +139,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
151 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe( 139 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe(
152 () => { 140 () => {
153 this.notifier.success($localize`Video ${entry.video.name} unblocked.`) 141 this.notifier.success($localize`Video ${entry.video.name} unblocked.`)
154 this.loadData() 142 this.reloadData()
155 }, 143 },
156 144
157 err => this.notifier.error(err.message) 145 err => this.notifier.error(err.message)
@@ -169,7 +157,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
169 ) 157 )
170 } 158 }
171 159
172 protected loadData () { 160 protected reloadData () {
173 this.videoBlocklistService.listBlocks({ 161 this.videoBlocklistService.listBlocks({
174 pagination: this.pagination, 162 pagination: this.pagination,
175 sort: this.sort, 163 sort: this.sort,
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
index d360c3c51..9d9283536 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
@@ -8,8 +8,9 @@
8<em i18n>This view also shows comments from muted accounts.</em> 8<em i18n>This view also shows comments from muted accounts.</em>
9 9
10<p-table 10<p-table
11 [value]="comments" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 11 [value]="comments" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
12 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" 12 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
13 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
13 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 14 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
14 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments" 15 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments"
15 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 16 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -26,25 +27,7 @@
26 </div> 27 </div>
27 28
28 <div class="ml-auto"> 29 <div class="ml-auto">
29 <div class="input-group has-feedback has-clear"> 30 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
30 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
31 <div class="input-group-text" ngbDropdownToggle>
32 <span class="caret" aria-haspopup="menu" role="button"></span>
33 </div>
34
35 <div role="menu" ngbDropdownMenu>
36 <h6 class="dropdown-header" i18n>Advanced comments filters</h6>
37 <a [routerLink]="[ '/admin/moderation/video-comments/list' ]" [queryParams]="{ 'search': 'local:true' }" class="dropdown-item" i18n>Local comments</a>
38 <a [routerLink]="[ '/admin/moderation/video-comments/list' ]" [queryParams]="{ 'search': 'local:false' }" class="dropdown-item" i18n>Remote comments</a>
39 </div>
40 </div>
41 <input
42 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
43 (keyup)="onSearch($event)"
44 >
45 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
46 <span class="sr-only" i18n>Clear filters</span>
47 </div>
48 </div> 31 </div>
49 </div> 32 </div>
50 </ng-template> 33 </ng-template>
@@ -86,7 +69,7 @@
86 <td> 69 <td>
87 <a [href]="videoComment.account.localUrl" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 70 <a [href]="videoComment.account.localUrl" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
88 <div class="chip two-lines"> 71 <div class="chip two-lines">
89 <my-account-avatar [account]="videoComment.account"></my-account-avatar> 72 <my-actor-avatar [account]="videoComment.account"></my-actor-avatar>
90 <div> 73 <div>
91 {{ videoComment.account.displayName }} 74 {{ videoComment.account.displayName }}
92 <span>{{ videoComment.by }}</span> 75 <span>{{ videoComment.by }}</span>
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
index c9262da09..a6f931e3b 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
@@ -11,23 +11,6 @@ my-global-icon {
11 height: 24px; 11 height: 24px;
12} 12}
13 13
14.input-group {
15 @include peertube-input-group(300px);
16
17 .dropdown-toggle::after {
18 margin-left: 0;
19 }
20}
21
22.caption {
23 justify-content: flex-end;
24
25 input {
26 @include peertube-input-text(250px);
27 flex-grow: 1;
28 }
29}
30
31.video { 14.video {
32 display: flex; 15 display: flex;
33 flex-direction: column; 16 flex-direction: column;
@@ -49,7 +32,8 @@ my-global-icon {
49 max-height: 22px; 32 max-height: 22px;
50 } 33 }
51 34
52 div, p { 35 div,
36 p {
53 @include ellipsis; 37 @include ellipsis;
54 } 38 }
55 39
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
index 63493d00d..e2ae993b0 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
@@ -2,6 +2,7 @@ import { SortMeta } from 'primeng/api'
2import { AfterViewInit, Component, OnInit } from '@angular/core' 2import { AfterViewInit, Component, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' 4import { AuthService, ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
5import { AdvancedInputFilter } from '@app/shared/shared-forms'
5import { DropdownAction } from '@app/shared/shared-main' 6import { DropdownAction } from '@app/shared/shared-main'
6import { BulkService } from '@app/shared/shared-moderation' 7import { BulkService } from '@app/shared/shared-moderation'
7import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment' 8import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment'
@@ -12,9 +13,7 @@ import { FeedFormat, UserRight } from '@shared/models'
12 templateUrl: './video-comment-list.component.html', 13 templateUrl: './video-comment-list.component.html',
13 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ] 14 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ]
14}) 15})
15export class VideoCommentListComponent extends RestTable implements OnInit, AfterViewInit { 16export class VideoCommentListComponent extends RestTable implements OnInit {
16 baseRoute = '/admin/moderation/video-comments/list'
17
18 comments: VideoCommentAdmin[] 17 comments: VideoCommentAdmin[]
19 totalRecords = 0 18 totalRecords = 0
20 sort: SortMeta = { field: 'createdAt', order: -1 } 19 sort: SortMeta = { field: 'createdAt', order: -1 }
@@ -43,6 +42,17 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
43 selectedComments: VideoCommentAdmin[] = [] 42 selectedComments: VideoCommentAdmin[] = []
44 bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = [] 43 bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = []
45 44
45 inputFilters: AdvancedInputFilter[] = [
46 {
47 queryParams: { 'search': 'local:true' },
48 label: $localize`Local comments`
49 },
50 {
51 queryParams: { 'search': 'local:false' },
52 label: $localize`Remote comments`
53 }
54 ]
55
46 get authUser () { 56 get authUser () {
47 return this.auth.getUser() 57 return this.auth.getUser()
48 } 58 }
@@ -79,7 +89,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
79 89
80 ngOnInit () { 90 ngOnInit () {
81 this.initialize() 91 this.initialize()
82 this.listenToSearchChange()
83 92
84 this.bulkCommentActions = [ 93 this.bulkCommentActions = [
85 { 94 {
@@ -91,10 +100,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
91 ] 100 ]
92 } 101 }
93 102
94 ngAfterViewInit () {
95 if (this.search) this.setTableFilter(this.search, false)
96 }
97
98 getIdentifier () { 103 getIdentifier () {
99 return 'VideoCommentListComponent' 104 return 'VideoCommentListComponent'
100 } 105 }
@@ -107,7 +112,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
107 return this.selectedComments.length !== 0 112 return this.selectedComments.length !== 0
108 } 113 }
109 114
110 protected loadData () { 115 protected reloadData () {
111 this.videoCommentService.getAdminVideoComments({ 116 this.videoCommentService.getAdminVideoComments({
112 pagination: this.pagination, 117 pagination: this.pagination,
113 sort: this.sort, 118 sort: this.sort,
@@ -135,7 +140,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
135 this.videoCommentService.deleteVideoComments(commentArgs).subscribe( 140 this.videoCommentService.deleteVideoComments(commentArgs).subscribe(
136 () => { 141 () => {
137 this.notifier.success($localize`${commentArgs.length} comments deleted.`) 142 this.notifier.success($localize`${commentArgs.length} comments deleted.`)
138 this.loadData() 143 this.reloadData()
139 }, 144 },
140 145
141 err => this.notifier.error(err.message), 146 err => this.notifier.error(err.message),
@@ -147,7 +152,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
147 private deleteComment (comment: VideoCommentAdmin) { 152 private deleteComment (comment: VideoCommentAdmin) {
148 this.videoCommentService.deleteVideoComment(comment.video.id, comment.id) 153 this.videoCommentService.deleteVideoComment(comment.video.id, comment.id)
149 .subscribe( 154 .subscribe(
150 () => this.loadData(), 155 () => this.reloadData(),
151 156
152 err => this.notifier.error(err.message) 157 err => this.notifier.error(err.message)
153 ) 158 )
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html
index 9cbec03a1..bc4c2ef88 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html
@@ -23,13 +23,17 @@
23 </a> 23 </a>
24 24
25 <div class="buttons"> 25 <div class="buttons">
26 <my-edit-button *ngIf="!isTheme(plugin)" [routerLink]="getShowRouterLink(plugin)" label="Settings" i18n-label></my-edit-button> 26 <my-edit-button
27 27 *ngIf="!isTheme(plugin)" [routerLink]="getShowRouterLink(plugin)" label="Settings" i18n-label
28 <my-button class="update-button" *ngIf="isUpdateAvailable(plugin)" (click)="update(plugin)" [loading]="isUpdating(plugin)" 28 [responsiveLabel]="true"
29 [label]="getUpdateLabel(plugin)" icon="refresh" [attr.disabled]="isUpdating(plugin)" 29 ></my-edit-button>
30
31 <my-button
32 class="update-button" *ngIf="isUpdateAvailable(plugin)" (click)="update(plugin)" [loading]="isUpdating(plugin)"
33 [label]="getUpdateLabel(plugin)" icon="refresh" [attr.disabled]="isUpdating(plugin)" [responsiveLabel]="true"
30 ></my-button> 34 ></my-button>
31 35
32 <my-delete-button (click)="uninstall(plugin)" label="Uninstall" i18n-label></my-delete-button> 36 <my-delete-button (click)="uninstall(plugin)" label="Uninstall" i18n-label [responsiveLabel]="true"></my-delete-button>
33 </div> 37 </div>
34 </div> 38 </div>
35 39
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss
index 9376e38b0..22d4a59ab 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss
@@ -1,6 +1,6 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.update-button[disabled="true"] ::ng-deep .action-button { 4.update-button[disabled=true] ::ng-deep .action-button {
5 cursor: default !important; 5 cursor: default !important;
6} 6}
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
index 727633399..6900e8717 100644
--- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
+++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
@@ -48,10 +48,15 @@
48 <span *ngIf="plugin.installed" class="badge badge-success">Installed</span> 48 <span *ngIf="plugin.installed" class="badge badge-success">Installed</span>
49 49
50 <div class="buttons"> 50 <div class="buttons">
51 <my-edit-button *ngIf="plugin.installed === true && !isThemeSearch()" [routerLink]="getShowRouterLink(plugin)" label="Settings" i18n-label></my-edit-button> 51 <my-edit-button
52 *ngIf="plugin.installed === true && !isThemeSearch()" [routerLink]="getShowRouterLink(plugin)"
53 label="Settings" i18n-label [responsiveLabel]="true"
54 ></my-edit-button>
52 55
53 <my-button class="update-button" *ngIf="plugin.installed === false" (click)="install(plugin)" [loading]="isInstalling(plugin)" 56 <my-button
54 label="Install" icon="cloud-download" [attr.disabled]="isInstalling(plugin)" 57 class="update-button" *ngIf="plugin.installed === false" (click)="install(plugin)"
58 [loading]="isInstalling(plugin)" label="Install" [responsiveLabel]="true"
59 icon="cloud-download" [attr.disabled]="isInstalling(plugin)"
55 ></my-button> 60 ></my-button>
56 </div> 61 </div>
57 </div> 62 </div>
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss
index 5ab6e5f1b..6b7b84e29 100644
--- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss
+++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss
@@ -5,7 +5,8 @@ h2 {
5 margin-bottom: 20px; 5 margin-bottom: 20px;
6} 6}
7 7
8input[type=submit], button { 8input[type=submit],
9button {
9 @include peertube-button; 10 @include peertube-button;
10 @include orange-button; 11 @include orange-button;
11 12
diff --git a/client/src/app/+admin/plugins/plugins.component.scss b/client/src/app/+admin/plugins/plugins.component.scss
deleted file mode 100644
index ce9825f53..000000000
--- a/client/src/app/+admin/plugins/plugins.component.scss
+++ /dev/null
@@ -1,28 +0,0 @@
1@import '_variables';
2@import '_mixins';
3
4@media screen and (max-width: $small-view) {
5 ::ng-deep .plugins .plugin .first-row {
6 flex-wrap: wrap;
7
8 .plugin-name,
9 .plugin-version,
10 .plugin-icon {
11 margin-bottom: 10px;
12 }
13
14 .buttons {
15 my-edit-button,
16 my-delete-button,
17 my-button {
18 .action-button {
19 padding: 0 13px;
20 }
21
22 .button-label {
23 display: none;
24 }
25 }
26 }
27 }
28}
diff --git a/client/src/app/+admin/plugins/plugins.component.ts b/client/src/app/+admin/plugins/plugins.component.ts
index 6ec6fa4a1..de06c0671 100644
--- a/client/src/app/+admin/plugins/plugins.component.ts
+++ b/client/src/app/+admin/plugins/plugins.component.ts
@@ -1,8 +1,7 @@
1import { Component } from '@angular/core' 1import { Component } from '@angular/core'
2 2
3@Component({ 3@Component({
4 templateUrl: './plugins.component.html', 4 templateUrl: './plugins.component.html'
5 styleUrls: [ './plugins.component.scss' ]
6}) 5})
7export class PluginsComponent { 6export class PluginsComponent {
8} 7}
diff --git a/client/src/app/+admin/plugins/shared/plugin-list.component.scss b/client/src/app/+admin/plugins/shared/plugin-list.component.scss
index f59a01b74..47cb1e6e5 100644
--- a/client/src/app/+admin/plugins/shared/plugin-list.component.scss
+++ b/client/src/app/+admin/plugins/shared/plugin-list.component.scss
@@ -27,7 +27,7 @@
27 my-global-icon { 27 my-global-icon {
28 @include apply-svg-color(pvar(--greyForegroundColor)); 28 @include apply-svg-color(pvar(--greyForegroundColor));
29 29
30 &[iconName="npm"] { 30 &[iconName=npm] {
31 @include fill-svg-color(pvar(--greyForegroundColor)); 31 @include fill-svg-color(pvar(--greyForegroundColor));
32 } 32 }
33 } 33 }
@@ -36,6 +36,7 @@
36 .buttons { 36 .buttons {
37 margin-left: auto; 37 margin-left: auto;
38 width: max-content; 38 width: max-content;
39
39 > *:not(:last-child) { 40 > *:not(:last-child) {
40 margin-right: 10px; 41 margin-right: 10px;
41 } 42 }
@@ -49,7 +50,7 @@
49 justify-content: space-between; 50 justify-content: space-between;
50 51
51 .description { 52 .description {
52 opacity: 0.8 53 opacity: 0.8;
53 } 54 }
54} 55}
55 56
@@ -57,3 +58,14 @@
57 @include peertube-button-link; 58 @include peertube-button-link;
58 @include button-with-icon(21px, 0, -2px); 59 @include button-with-icon(21px, 0, -2px);
59} 60}
61
62@media screen and (max-width: $small-view) {
63 .first-row {
64 flex-wrap: wrap;
65
66 .buttons {
67 flex-basis: 100%;
68 margin-top: 10px;
69 }
70 }
71}
diff --git a/client/src/app/+admin/system/jobs/jobs.component.scss b/client/src/app/+admin/system/jobs/jobs.component.scss
index 7c6159420..65ee6ec5f 100644
--- a/client/src/app/+admin/system/jobs/jobs.component.scss
+++ b/client/src/app/+admin/system/jobs/jobs.component.scss
@@ -51,7 +51,7 @@ pre {
51} 51}
52 52
53.job-error { 53.job-error {
54 color: red; 54 color: #ff0000;
55} 55}
56 56
57.badge { 57.badge {
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts
index 43578eedd..29ba95c5c 100644
--- a/client/src/app/+admin/system/jobs/jobs.component.ts
+++ b/client/src/app/+admin/system/jobs/jobs.component.ts
@@ -86,7 +86,7 @@ export class JobsComponent extends RestTable implements OnInit {
86 onJobStateOrTypeChanged () { 86 onJobStateOrTypeChanged () {
87 this.pagination.start = 0 87 this.pagination.start = 0
88 88
89 this.loadData() 89 this.reloadData()
90 this.saveJobStateAndType() 90 this.saveJobStateAndType()
91 } 91 }
92 92
@@ -104,10 +104,10 @@ export class JobsComponent extends RestTable implements OnInit {
104 this.jobs = [] 104 this.jobs = []
105 this.totalRecords = 0 105 this.totalRecords = 0
106 106
107 this.loadData() 107 this.reloadData()
108 } 108 }
109 109
110 protected loadData () { 110 protected reloadData () {
111 let jobState = this.jobState as JobState 111 let jobState = this.jobState as JobState
112 if (this.jobState === 'all') jobState = null 112 if (this.jobState === 'all') jobState = null
113 113
diff --git a/client/src/app/+admin/system/logs/logs.component.scss b/client/src/app/+admin/system/logs/logs.component.scss
index 587a9795c..1a7c3be75 100644
--- a/client/src/app/+admin/system/logs/logs.component.scss
+++ b/client/src/app/+admin/system/logs/logs.component.scss
@@ -66,7 +66,7 @@
66 ng-select, 66 ng-select,
67 my-button { 67 my-button {
68 width: 100% !important; 68 width: 100% !important;
69 margin-left: 0px !important; 69 margin-left: 0 !important;
70 margin-bottom: 10px !important; 70 margin-bottom: 10px !important;
71 } 71 }
72 72
@@ -85,7 +85,7 @@
85 ng-select, 85 ng-select,
86 my-button { 86 my-button {
87 width: 100% !important; 87 width: 100% !important;
88 margin-left: 0px !important; 88 margin-left: 0 !important;
89 margin-bottom: 10px !important; 89 margin-bottom: 10px !important;
90 } 90 }
91 91
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss
index 8b0ac8783..145177fb9 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.scss
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss
@@ -37,7 +37,8 @@ my-select-custom-value {
37 display: block; 37 display: block;
38} 38}
39 39
40input[type=submit], button { 40input[type=submit],
41button {
41 @include peertube-button; 42 @include peertube-button;
42 @include orange-button; 43 @include orange-button;
43 44
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.scss b/client/src/app/+admin/users/user-edit/user-password.component.scss
index 1f0d49227..66d15ee9c 100644
--- a/client/src/app/+admin/users/user-edit/user-password.component.scss
+++ b/client/src/app/+admin/users/user-edit/user-password.component.scss
@@ -7,7 +7,7 @@ input:not([type=submit]):not([type=checkbox]) {
7 display: block; 7 display: block;
8 border-top-right-radius: 0; 8 border-top-right-radius: 0;
9 border-bottom-right-radius: 0; 9 border-bottom-right-radius: 0;
10 border-right: none; 10 border-right: 0;
11} 11}
12 12
13input[type=submit] { 13input[type=submit] {
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index e114f3425..44d8a7e87 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -1,7 +1,7 @@
1<p-table 1<p-table
2 [value]="users" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="users" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" 3 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" [(selection)]="selectedUsers"
4 [(selection)]="selectedUsers" 4 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users" 6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users"
7 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 7 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -22,24 +22,7 @@
22 </div> 22 </div>
23 23
24 <div class="ml-auto"> 24 <div class="ml-auto">
25 <div class="input-group has-feedback has-clear"> 25 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
26 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
27 <div class="input-group-text" ngbDropdownToggle>
28 <span class="caret" aria-haspopup="menu" role="button"></span>
29 </div>
30
31 <div role="menu" ngbDropdownMenu>
32 <h6 class="dropdown-header" i18n>Advanced user filters</h6>
33 <a [routerLink]="[ '/admin/users/list' ]" [queryParams]="{ 'search': 'banned:true' }" class="dropdown-item" i18n>Banned users</a>
34 </div>
35 </div>
36 <input
37 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
38 (keyup)="onSearch($event)"
39 >
40 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
41 <span class="sr-only" i18n>Clear filters</span>
42 </div>
43 </div> 26 </div>
44 27
45 </div> 28 </div>
@@ -106,7 +89,7 @@
106 <td *ngIf="isSelected('username')"> 89 <td *ngIf="isSelected('username')">
107 <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]"> 90 <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
108 <div class="chip two-lines"> 91 <div class="chip two-lines">
109 <my-account-avatar [account]="user?.account"></my-account-avatar> 92 <my-actor-avatar [account]="user?.account" size="32"></my-actor-avatar>
110 <div> 93 <div>
111 <span class="user-table-primary-text">{{ user.account.displayName }}</span> 94 <span class="user-table-primary-text">{{ user.account.displayName }}</span>
112 <span class="text-muted">{{ user.username }}</span> 95 <span class="text-muted">{{ user.username }}</span>
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss
index 50080bad6..db4979a51 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/users/user-list/user-list.component.scss
@@ -11,6 +11,7 @@ tr.banned > td {
11 11
12.table-email { 12.table-email {
13 @include disable-default-a-behaviour; 13 @include disable-default-a-behaviour;
14
14 color: pvar(--mainForegroundColor); 15 color: pvar(--mainForegroundColor);
15} 16}
16 17
@@ -24,18 +25,10 @@ tr.banned > td {
24 25
25.user-table-primary-text .glyphicon { 26.user-table-primary-text .glyphicon {
26 font-size: 80%; 27 font-size: 80%;
27 color: gray; 28 color: #808080;
28 margin-left: 0.1rem; 29 margin-left: 0.1rem;
29} 30}
30 31
31.caption {
32 justify-content: space-between;
33
34 input {
35 @include peertube-input-text(250px);
36 }
37}
38
39p-tableCheckbox { 32p-tableCheckbox {
40 position: relative; 33 position: relative;
41 top: -2.5px; 34 top: -2.5px;
@@ -55,18 +48,7 @@ my-global-icon {
55 48
56.progress { 49.progress {
57 @include progressbar($small: true); 50 @include progressbar($small: true);
51
58 width: auto; 52 width: auto;
59 max-width: 100%; 53 max-width: 100%;
60} 54}
61
62.input-group {
63 @include peertube-input-group(300px);
64
65 input {
66 flex: 1;
67 }
68
69 .dropdown-toggle::after {
70 margin-left: 0;
71 }
72}
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index 339e18206..1c60adf89 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -1,8 +1,9 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { Component, OnInit, ViewChild } from '@angular/core' 2import { Component, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Params, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core' 4import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core'
5import { Account, DropdownAction } from '@app/shared/shared-main' 5import { AdvancedInputFilter } from '@app/shared/shared-forms'
6import { DropdownAction } from '@app/shared/shared-main'
6import { UserBanModalComponent } from '@app/shared/shared-moderation' 7import { UserBanModalComponent } from '@app/shared/shared-moderation'
7import { ServerConfig, User, UserRole } from '@shared/models' 8import { ServerConfig, User, UserRole } from '@shared/models'
8 9
@@ -22,15 +23,24 @@ export class UserListComponent extends RestTable implements OnInit {
22 @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent 23 @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent
23 24
24 users: User[] = [] 25 users: User[] = []
26
25 totalRecords = 0 27 totalRecords = 0
26 sort: SortMeta = { field: 'createdAt', order: 1 } 28 sort: SortMeta = { field: 'createdAt', order: 1 }
27 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 29 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
30
28 highlightBannedUsers = false 31 highlightBannedUsers = false
29 32
30 selectedUsers: User[] = [] 33 selectedUsers: User[] = []
31 bulkUserActions: DropdownAction<User[]>[][] = [] 34 bulkUserActions: DropdownAction<User[]>[][] = []
32 columns: { id: string, label: string }[] 35 columns: { id: string, label: string }[]
33 36
37 inputFilters: AdvancedInputFilter[] = [
38 {
39 queryParams: { 'search': 'banned:true' },
40 label: $localize`Banned users`
41 }
42 ]
43
34 private _selectedColumns: string[] 44 private _selectedColumns: string[]
35 private serverConfig: ServerConfig 45 private serverConfig: ServerConfig
36 46
@@ -68,7 +78,6 @@ export class UserListComponent extends RestTable implements OnInit {
68 .subscribe(config => this.serverConfig = config) 78 .subscribe(config => this.serverConfig = config)
69 79
70 this.initialize() 80 this.initialize()
71 this.listenToSearchChange()
72 81
73 this.bulkUserActions = [ 82 this.bulkUserActions = [
74 [ 83 [
@@ -160,7 +169,7 @@ export class UserListComponent extends RestTable implements OnInit {
160 } 169 }
161 170
162 onUserChanged () { 171 onUserChanged () {
163 this.loadData() 172 this.reloadData()
164 } 173 }
165 174
166 async unbanUsers (users: User[]) { 175 async unbanUsers (users: User[]) {
@@ -171,7 +180,7 @@ export class UserListComponent extends RestTable implements OnInit {
171 .subscribe( 180 .subscribe(
172 () => { 181 () => {
173 this.notifier.success($localize`${users.length} users unbanned.`) 182 this.notifier.success($localize`${users.length} users unbanned.`)
174 this.loadData() 183 this.reloadData()
175 }, 184 },
176 185
177 err => this.notifier.error(err.message) 186 err => this.notifier.error(err.message)
@@ -193,7 +202,7 @@ export class UserListComponent extends RestTable implements OnInit {
193 this.userService.removeUser(users).subscribe( 202 this.userService.removeUser(users).subscribe(
194 () => { 203 () => {
195 this.notifier.success($localize`${users.length} users deleted.`) 204 this.notifier.success($localize`${users.length} users deleted.`)
196 this.loadData() 205 this.reloadData()
197 }, 206 },
198 207
199 err => this.notifier.error(err.message) 208 err => this.notifier.error(err.message)
@@ -204,7 +213,7 @@ export class UserListComponent extends RestTable implements OnInit {
204 this.userService.updateUsers(users, { emailVerified: true }).subscribe( 213 this.userService.updateUsers(users, { emailVerified: true }).subscribe(
205 () => { 214 () => {
206 this.notifier.success($localize`${users.length} users email set as verified.`) 215 this.notifier.success($localize`${users.length} users email set as verified.`)
207 this.loadData() 216 this.reloadData()
208 }, 217 },
209 218
210 err => this.notifier.error(err.message) 219 err => this.notifier.error(err.message)
@@ -215,7 +224,7 @@ export class UserListComponent extends RestTable implements OnInit {
215 return this.selectedUsers.length !== 0 224 return this.selectedUsers.length !== 0
216 } 225 }
217 226
218 protected loadData () { 227 protected reloadData () {
219 this.selectedUsers = [] 228 this.selectedUsers = []
220 229
221 this.userService.getUsers({ 230 this.userService.getUsers({
diff --git a/client/src/app/+login/login.component.scss b/client/src/app/+login/login.component.scss
index eddaff542..62ae722c3 100644
--- a/client/src/app/+login/login.component.scss
+++ b/client/src/app/+login/login.component.scss
@@ -33,7 +33,8 @@ input[type=email] {
33 } 33 }
34} 34}
35 35
36.create-an-account, .forgot-password-button { 36.create-an-account,
37.forgot-password-button {
37 color: pvar(--mainForegroundColor); 38 color: pvar(--mainForegroundColor);
38 cursor: pointer; 39 cursor: pointer;
39 transition: opacity cubic-bezier(0.39, 0.575, 0.565, 1); 40 transition: opacity cubic-bezier(0.39, 0.575, 0.565, 1);
@@ -49,7 +50,7 @@ input[type=email] {
49 justify-content: space-around; 50 justify-content: space-around;
50 flex-wrap: wrap; 51 flex-wrap: wrap;
51 52
52 & > div { 53 > div {
53 flex: 1 1; 54 flex: 1 1;
54 } 55 }
55 56
@@ -65,7 +66,8 @@ input[type=email] {
65 form { 66 form {
66 margin: 0; 67 margin: 0;
67 68
68 &, input { 69 &,
70 input {
69 width: 100%; 71 width: 100%;
70 } 72 }
71 73
@@ -82,7 +84,8 @@ input[type=email] {
82 84
83 color: var(--mainColor); 85 color: var(--mainColor);
84 86
85 &:hover, &:active { 87 &:hover,
88 &:active {
86 color: var(--mainHoverColor); 89 color: var(--mainHoverColor);
87 } 90 }
88 } 91 }
@@ -111,7 +114,7 @@ input[type=email] {
111 min-width: 100px; 114 min-width: 100px;
112 115
113 &:hover { 116 &:hover {
114 background-color: rgba(209, 215, 224, 0.5) 117 background-color: rgba(209, 215, 224, 0.5);
115 } 118 }
116 } 119 }
117 } 120 }
@@ -138,7 +141,7 @@ input[type=email] {
138 } 141 }
139} 142}
140 143
141@mixin columnReverseDisplay { 144@mixin column-reverse-display {
142 flex-direction: column-reverse; 145 flex-direction: column-reverse;
143 146
144 .login-form-and-externals, 147 .login-form-and-externals,
@@ -168,14 +171,14 @@ input[type=email] {
168 171
169@media screen and (max-width: breakpoint(md)) { 172@media screen and (max-width: breakpoint(md)) {
170 .wrapper { 173 .wrapper {
171 @include columnReverseDisplay(); 174 @include column-reverse-display();
172 } 175 }
173} 176}
174 177
175@media screen and (max-width: breakpoint(md) + $menu-width) { 178@media screen and (max-width: breakpoint(md) + $menu-width) {
176 :host-context(.main-col:not(.expanded)) { 179 :host-context(.main-col:not(.expanded)) {
177 .wrapper { 180 .wrapper {
178 @include columnReverseDisplay(); 181 @include column-reverse-display();
179 } 182 }
180 } 183 }
181} 184}
diff --git a/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html b/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html
index 59ca61be6..e83b59019 100644
--- a/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html
+++ b/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html
@@ -3,4 +3,4 @@
3 <ng-container i18n>Reports</ng-container> 3 <ng-container i18n>Reports</ng-container>
4</h1> 4</h1>
5 5
6<my-abuse-list-table viewType="user" baseRoute="/my-account/abuses"></my-abuse-list-table> 6<my-abuse-list-table viewType="user"></my-abuse-list-table>
diff --git a/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss b/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss
index 704132c03..c1e1f2432 100644
--- a/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss
+++ b/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss
@@ -21,7 +21,7 @@ input[type=submit] {
21 display: flex; 21 display: flex;
22 margin-left: auto; 22 margin-left: auto;
23 23
24 & + .form-error { 24 + .form-error {
25 display: inline; 25 display: inline;
26 margin-left: 5px; 26 margin-left: 5px;
27 } 27 }
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
index 035fa2b27..abf52504a 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
@@ -32,7 +32,8 @@ my-user-notifications {
32 .header { 32 .header {
33 flex-direction: column; 33 flex-direction: column;
34 34
35 & >:first-child, .peertube-select-container { 35 > :first-child,
36 .peertube-select-container {
36 margin-bottom: 15px; 37 margin-bottom: 15px;
37 } 38 }
38 39
diff --git a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss
index d79ff690b..64f960964 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss
+++ b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss
@@ -7,4 +7,4 @@
7 @include danger-button; 7 @include danger-button;
8 @include disable-outline; 8 @include disable-outline;
9 } 9 }
10} \ No newline at end of file 10}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss
index b06d8b16d..42319400d 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss
@@ -10,7 +10,7 @@
10 font-size: 16px; 10 font-size: 16px;
11 } 11 }
12 12
13 & > div { 13 > div {
14 padding: 10px; 14 padding: 10px;
15 15
16 &:first-child { 16 &:first-child {
diff --git a/client/src/app/+my-account/my-account.component.scss b/client/src/app/+my-account/my-account.component.scss
index a5bb499b4..b32bc84e7 100644
--- a/client/src/app/+my-account/my-account.component.scss
+++ b/client/src/app/+my-account/my-account.component.scss
@@ -2,12 +2,12 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.row { 4.row {
5 @include sub-menu-h1;
6
5 flex-direction: column; 7 flex-direction: column;
6 width: 100%; 8 width: 100%;
7 9
8 & > my-top-menu-dropdown:nth-child(1) { 10 > my-top-menu-dropdown:nth-child(1) {
9 flex-grow: 1; 11 flex-grow: 1;
10 } 12 }
11
12 @include sub-menu-h1;
13} 13}
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts
index 050cd4b34..4081e4f01 100644
--- a/client/src/app/+my-account/my-account.module.ts
+++ b/client/src/app/+my-account/my-account.module.ts
@@ -3,13 +3,14 @@ import { TableModule } from 'primeng/table'
3import { DragDropModule } from '@angular/cdk/drag-drop' 3import { DragDropModule } from '@angular/cdk/drag-drop'
4import { NgModule } from '@angular/core' 4import { NgModule } from '@angular/core'
5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' 5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
6import { SharedActorImageModule } from '@app/shared/shared-actor-image' 6import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit'
7import { SharedFormModule } from '@app/shared/shared-forms' 7import { SharedFormModule } from '@app/shared/shared-forms'
8import { SharedGlobalIconModule } from '@app/shared/shared-icons' 8import { SharedGlobalIconModule } from '@app/shared/shared-icons'
9import { SharedMainModule } from '@app/shared/shared-main' 9import { SharedMainModule } from '@app/shared/shared-main'
10import { SharedModerationModule } from '@app/shared/shared-moderation' 10import { SharedModerationModule } from '@app/shared/shared-moderation'
11import { SharedShareModal } from '@app/shared/shared-share-modal' 11import { SharedShareModal } from '@app/shared/shared-share-modal'
12import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' 12import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings'
13import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
13import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component' 14import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component'
14import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component' 15import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component'
15import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component' 16import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component'
@@ -23,7 +24,6 @@ import { MyAccountNotificationPreferencesComponent } from './my-account-settings
23import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component' 24import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component'
24import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' 25import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
25import { MyAccountComponent } from './my-account.component' 26import { MyAccountComponent } from './my-account.component'
26import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
27 27
28@NgModule({ 28@NgModule({
29 imports: [ 29 imports: [
@@ -40,8 +40,8 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
40 SharedGlobalIconModule, 40 SharedGlobalIconModule,
41 SharedAbuseListModule, 41 SharedAbuseListModule,
42 SharedShareModal, 42 SharedShareModal,
43 SharedAccountAvatarModule, 43 SharedActorImageModule,
44 SharedActorImageModule 44 SharedActorImageEditModule
45 ], 45 ],
46 46
47 declarations: [ 47 declarations: [
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss
index 22de103d1..667726c22 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss
@@ -66,7 +66,8 @@ textarea {
66 width: auto !important; 66 width: auto !important;
67 } 67 }
68 68
69 label[for=name] + div, textarea { 69 label[for=name] + div,
70 textarea {
70 width: 100%; 71 width: 100%;
71 } 72 }
72} 73}
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
index b704a1cc6..e41cbe921 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
@@ -1,18 +1,11 @@
1<h1> 1<h1>
2 <span> 2 <my-global-icon iconName="channel" aria-hidden="true"></my-global-icon>
3 <my-global-icon iconName="channel" aria-hidden="true"></my-global-icon> 3 <ng-container i18n>My channels</ng-container>
4 <ng-container i18n>My channels</ng-container> 4 <span class="badge badge-secondary">{{ totalItems }}</span>
5 <span class="badge badge-secondary">{{ totalItems }}</span>
6 </span>
7</h1> 5</h1>
8 6
9<div class="video-channels-header d-flex justify-content-between"> 7<div class="video-channels-header d-flex justify-content-between">
10 <div class="has-feedback has-clear"> 8 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11 <input type="text" placeholder="Search your channels" i18n-placeholder [(ngModel)]="channelsSearch"
12 (ngModelChange)="onChannelsSearchChanged()" />
13 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
14 <span class="sr-only" i18n>Clear filters</span>
15 </div>
16 9
17 <a class="create-button" routerLink="create"> 10 <a class="create-button" routerLink="create">
18 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 11 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
@@ -20,11 +13,11 @@
20 </a> 13 </a>
21</div> 14</div>
22 15
16<div class="no-results" i18n *ngIf="totalItems === 0">No channel found.</div>
17
23<div class="video-channels"> 18<div class="video-channels">
24 <div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel"> 19 <div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel">
25 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"> 20 <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/video-channels', videoChannel.nameWithHost ]"></my-actor-avatar>
26 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
27 </a>
28 21
29 <div class="video-channel-info"> 22 <div class="video-channel-info">
30 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> 23 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page">
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
index 8804fa95c..191c5169d 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
@@ -1,6 +1,11 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4h1 my-global-icon {
5 position: relative;
6 top: -2px;
7}
8
4.create-button { 9.create-button {
5 @include create-button; 10 @include create-button;
6} 11}
@@ -9,10 +14,8 @@ input[type=text] {
9 @include peertube-input-text(300px); 14 @include peertube-input-text(300px);
10} 15}
11 16
12::ng-deep .action-button { 17my-edit-button {
13 &.action-button-edit { 18 margin-right: 10px;
14 margin-right: 10px;
15 }
16} 19}
17 20
18.video-channel { 21.video-channel {
@@ -20,40 +23,40 @@ input[type=text] {
20 23
21 padding-bottom: 0; 24 padding-bottom: 0;
22 25
23 img { 26 my-actor-avatar {
24 @include channel-avatar(80px); 27 @include actor-avatar-size(80px);
25 28
26 margin-right: 10px; 29 margin-right: 10px;
27 } 30 }
31}
28 32
29 .video-channel-info { 33.video-channel-info {
30 flex-grow: 1; 34 flex-grow: 1;
31 35}
32 a.video-channel-names {
33 @include disable-default-a-behaviour;
34
35 width: fit-content;
36 display: flex;
37 align-items: baseline;
38 color: pvar(--mainForegroundColor);
39
40 .video-channel-display-name {
41 font-weight: $font-semibold;
42 font-size: 18px;
43 }
44
45 .video-channel-name {
46 font-size: 14px;
47 color: $grey-actor-name;
48 margin-left: 5px;
49 }
50 }
51 }
52 36
53 .video-channel-buttons { 37.video-channel-names {
54 margin-top: 10px; 38 @include disable-default-a-behaviour;
55 min-width: 190px; 39
56 } 40 width: fit-content;
41 display: flex;
42 align-items: baseline;
43 color: pvar(--mainForegroundColor);
44}
45
46.video-channel-display-name {
47 font-weight: $font-semibold;
48 font-size: 18px;
49}
50
51.video-channel-name {
52 font-size: 14px;
53 color: $grey-actor-name;
54 margin-left: 5px;
55}
56
57.video-channel-buttons {
58 margin-top: 10px;
59 min-width: 190px;
57} 60}
58 61
59::ng-deep .chartjs-render-monitor { 62::ng-deep .chartjs-render-monitor {
@@ -73,21 +76,6 @@ input[type=text] {
73 .video-channel { 76 .video-channel {
74 padding-bottom: 10px; 77 padding-bottom: 10px;
75 78
76 .video-channel-info {
77 padding-bottom: 10px;
78 text-align: center;
79
80 .video-channel-names {
81 flex-direction: column;
82 align-items: center !important;
83 margin: auto;
84
85 .video-channel-name {
86 margin-left: 0px !important;
87 }
88 }
89 }
90
91 img { 79 img {
92 margin-right: 0; 80 margin-right: 0;
93 } 81 }
@@ -96,6 +84,21 @@ input[type=text] {
96 align-self: center; 84 align-self: center;
97 } 85 }
98 } 86 }
87
88 .video-channel-info {
89 padding-bottom: 10px;
90 text-align: center;
91 }
92
93 .video-channel-names {
94 flex-direction: column;
95 align-items: center !important;
96 margin: auto;
97 }
98
99 .video-channel-name {
100 margin-left: 0 !important;
101 }
99} 102}
100 103
101@media screen and (max-width: $mobile-view) { 104@media screen and (max-width: $mobile-view) {
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
index f6ba50a48..9e3bf35b4 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
@@ -1,29 +1,26 @@
1import { ChartData } from 'chart.js' 1import { ChartData } from 'chart.js'
2import { max, maxBy, min, minBy } from 'lodash-es' 2import { max, maxBy, min, minBy } from 'lodash-es'
3import { Subject } from 'rxjs' 3import { mergeMap } from 'rxjs/operators'
4import { debounceTime, mergeMap } from 'rxjs/operators' 4import { Component } from '@angular/core'
5import { Component, OnInit } from '@angular/core' 5import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core'
6import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core'
7import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
8 7
9@Component({ 8@Component({
10 templateUrl: './my-video-channels.component.html', 9 templateUrl: './my-video-channels.component.html',
11 styleUrls: [ './my-video-channels.component.scss' ] 10 styleUrls: [ './my-video-channels.component.scss' ]
12}) 11})
13export class MyVideoChannelsComponent implements OnInit { 12export class MyVideoChannelsComponent {
14 totalItems: number 13 totalItems: number
15 14
16 videoChannels: VideoChannel[] = [] 15 videoChannels: VideoChannel[] = []
16
17 videoChannelsChartData: ChartData[] 17 videoChannelsChartData: ChartData[]
18 videoChannelsMinimumDailyViews = 0 18 videoChannelsMinimumDailyViews = 0
19 videoChannelsMaximumDailyViews: number 19 videoChannelsMaximumDailyViews: number
20 20
21 channelsSearch: string
22 channelsSearchChanged = new Subject<string>()
23
24 chartOptions: any 21 chartOptions: any
25 22
26 private user: User 23 search: string
27 24
28 constructor ( 25 constructor (
29 private authService: AuthService, 26 private authService: AuthService,
@@ -31,31 +28,15 @@ export class MyVideoChannelsComponent implements OnInit {
31 private confirmService: ConfirmService, 28 private confirmService: ConfirmService,
32 private videoChannelService: VideoChannelService, 29 private videoChannelService: VideoChannelService,
33 private screenService: ScreenService 30 private screenService: ScreenService
34 ) {} 31 ) {}
35
36 ngOnInit () {
37 this.user = this.authService.getUser()
38
39 this.loadVideoChannels()
40
41 this.channelsSearchChanged
42 .pipe(debounceTime(500))
43 .subscribe(() => {
44 this.loadVideoChannels()
45 })
46 }
47 32
48 get isInSmallView () { 33 get isInSmallView () {
49 return this.screenService.isInSmallView() 34 return this.screenService.isInSmallView()
50 } 35 }
51 36
52 resetSearch () { 37 onSearch (search: string) {
53 this.channelsSearch = '' 38 this.search = search
54 this.onChannelsSearchChanged() 39 this.loadVideoChannels()
55 }
56
57 onChannelsSearchChanged () {
58 this.channelsSearchChanged.next()
59 } 40 }
60 41
61 async deleteVideoChannel (videoChannel: VideoChannel) { 42 async deleteVideoChannel (videoChannel: VideoChannel) {
@@ -85,8 +66,11 @@ channel with the same name (${videoChannel.name})!`,
85 66
86 private loadVideoChannels () { 67 private loadVideoChannels () {
87 this.authService.userInformationLoaded 68 this.authService.userInformationLoaded
88 .pipe(mergeMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true, this.channelsSearch))) 69 .pipe(mergeMap(() => {
89 .subscribe(res => { 70 const user = this.authService.getUser()
71
72 return this.videoChannelService.listAccountVideoChannels(user.account, null, true, this.search)
73 })).subscribe(res => {
90 this.videoChannels = res.data 74 this.videoChannels = res.data
91 this.totalItems = res.total 75 this.totalItems = res.total
92 76
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts
index 53557ca02..c775bfdee 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts
@@ -1,6 +1,6 @@
1import { ChartModule } from 'primeng/chart' 1import { ChartModule } from 'primeng/chart'
2import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
3import { SharedActorImageModule } from '@app/shared/shared-actor-image' 3import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit'
4import { SharedFormModule } from '@app/shared/shared-forms' 4import { SharedFormModule } from '@app/shared/shared-forms'
5import { SharedGlobalIconModule } from '@app/shared/shared-icons' 5import { SharedGlobalIconModule } from '@app/shared/shared-icons'
6import { SharedMainModule } from '@app/shared/shared-main' 6import { SharedMainModule } from '@app/shared/shared-main'
@@ -8,6 +8,7 @@ import { MyVideoChannelCreateComponent } from './my-video-channel-create.compone
8import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component' 8import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component'
9import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module' 9import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module'
10import { MyVideoChannelsComponent } from './my-video-channels.component' 10import { MyVideoChannelsComponent } from './my-video-channels.component'
11import { SharedActorImageModule } from '@app/shared/shared-actor-image/shared-actor-image.module'
11 12
12@NgModule({ 13@NgModule({
13 imports: [ 14 imports: [
@@ -18,6 +19,7 @@ import { MyVideoChannelsComponent } from './my-video-channels.component'
18 SharedMainModule, 19 SharedMainModule,
19 SharedFormModule, 20 SharedFormModule,
20 SharedGlobalIconModule, 21 SharedGlobalIconModule,
22 SharedActorImageEditModule,
21 SharedActorImageModule 23 SharedActorImageModule
22 ], 24 ],
23 25
diff --git a/client/src/app/+my-library/my-history/my-history.component.html b/client/src/app/+my-library/my-history/my-history.component.html
index 9dec64645..45ca37e0d 100644
--- a/client/src/app/+my-library/my-history/my-history.component.html
+++ b/client/src/app/+my-library/my-history/my-history.component.html
@@ -5,14 +5,7 @@
5 5
6<div class="top-buttons"> 6<div class="top-buttons">
7 <div class="search-wrapper"> 7 <div class="search-wrapper">
8 <div class="input-group has-feedback has-clear"> 8 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
9 <input
10 type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history"
11 (keyup)="onSearch($event)"
12 >
13 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
14 <span class="sr-only" i18n>Clear filters</span>
15 </div>
16 </div> 9 </div>
17 10
18 <div class="history-switch"> 11 <div class="history-switch">
@@ -26,14 +19,15 @@
26 </button> 19 </button>
27</div> 20</div>
28 21
29 22<my-videos-selection
30<div class="no-history" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">You don't have any video in your watch history yet.</div> 23 [pagination]="pagination"
31 24 [(videosModel)]="videos"
32<div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()" class="videos"> 25 [miniatureDisplayOptions]="miniatureDisplayOptions"
33 <div class="video" *ngFor="let video of videos"> 26 [titlePage]="titlePage"
34 <my-video-miniature 27 [getVideosObservableFunction]="getVideosObservableFunction"
35 [video]="video" [displayAsRow]="true" 28 [user]="user"
36 (videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)" 29 [loadOnInit]="false"
37 ></my-video-miniature> 30 i18n-noResultMessage noResultMessage="You don't have any video in your watch history yet."
38 </div> 31 [enableSelection]="false"
39</div> 32 #videosSelection
33></my-videos-selection>
diff --git a/client/src/app/+my-library/my-history/my-history.component.scss b/client/src/app/+my-library/my-history/my-history.component.scss
index af4a34b4b..28b809f71 100644
--- a/client/src/app/+my-library/my-history/my-history.component.scss
+++ b/client/src/app/+my-library/my-history/my-history.component.scss
@@ -39,12 +39,12 @@
39 } 39 }
40 40
41 .delete-history { 41 .delete-history {
42 grid-column: 4;
43
44 @include peertube-button; 42 @include peertube-button;
45 @include grey-button; 43 @include grey-button;
46 @include button-with-icon; 44 @include button-with-icon;
47 45
46 grid-column: 4;
47
48 font-size: 15px; 48 font-size: 15px;
49 } 49 }
50} 50}
diff --git a/client/src/app/+my-library/my-history/my-history.component.ts b/client/src/app/+my-library/my-history/my-history.component.ts
index 1695bd7ad..ad83db7ab 100644
--- a/client/src/app/+my-library/my-history/my-history.component.ts
+++ b/client/src/app/+my-library/my-history/my-history.component.ts
@@ -1,36 +1,55 @@
1import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' 1import { Subject } from 'rxjs'
2import { tap } from 'rxjs/operators'
3import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
3import { 5import {
4 AuthService, 6 AuthService,
5 ComponentPagination, 7 ComponentPagination,
6 ConfirmService, 8 ConfirmService,
9 DisableForReuseHook,
7 LocalStorageService, 10 LocalStorageService,
8 Notifier, 11 Notifier,
9 ScreenService, 12 ScreenService,
10 ServerService, 13 ServerService,
14 User,
11 UserService 15 UserService
12} from '@app/core' 16} from '@app/core'
13import { immutableAssign } from '@app/helpers' 17import { immutableAssign } from '@app/helpers'
14import { UserHistoryService } from '@app/shared/shared-main' 18import { UserHistoryService, Video } from '@app/shared/shared-main'
15import { AbstractVideoList } from '@app/shared/shared-video-miniature' 19import { MiniatureDisplayOptions, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
16import { Subject } from 'rxjs'
17import { debounceTime, tap, distinctUntilChanged } from 'rxjs/operators'
18 20
19@Component({ 21@Component({
20 templateUrl: './my-history.component.html', 22 templateUrl: './my-history.component.html',
21 styleUrls: [ './my-history.component.scss' ] 23 styleUrls: [ './my-history.component.scss' ]
22}) 24})
23export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy { 25export class MyHistoryComponent implements OnInit, DisableForReuseHook {
26 @ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent
27
24 titlePage: string 28 titlePage: string
25 pagination: ComponentPagination = { 29 pagination: ComponentPagination = {
26 currentPage: 1, 30 currentPage: 1,
27 itemsPerPage: 5, 31 itemsPerPage: 5,
28 totalItems: null 32 totalItems: null
29 } 33 }
34
30 videosHistoryEnabled: boolean 35 videosHistoryEnabled: boolean
31 search: string
32 36
33 protected searchStream: Subject<string> 37 miniatureDisplayOptions: MiniatureDisplayOptions = {
38 date: true,
39 views: true,
40 by: true,
41 privacyLabel: false,
42 privacyText: true,
43 state: true,
44 blacklistInfo: true
45 }
46
47 getVideosObservableFunction = this.getVideosObservable.bind(this)
48
49 user: User
50
51 videos: Video[] = []
52 search: string
34 53
35 constructor ( 54 constructor (
36 protected router: Router, 55 protected router: Router,
@@ -45,45 +64,31 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD
45 private userHistoryService: UserHistoryService, 64 private userHistoryService: UserHistoryService,
46 protected cfr: ComponentFactoryResolver 65 protected cfr: ComponentFactoryResolver
47 ) { 66 ) {
48 super()
49
50 this.titlePage = $localize`My watch history` 67 this.titlePage = $localize`My watch history`
51 } 68 }
52 69
53 ngOnInit () { 70 ngOnInit () {
54 super.ngOnInit() 71 this.user = this.authService.getUser()
55 72
56 this.authService.userInformationLoaded 73 this.authService.userInformationLoaded
57 .subscribe(() => { 74 .subscribe(() => this.videosHistoryEnabled = this.user.videosHistoryEnabled)
58 this.videosHistoryEnabled = this.authService.getUser().videosHistoryEnabled 75 }
59 })
60
61 this.searchStream = new Subject()
62 76
63 this.searchStream 77 disableForReuse () {
64 .pipe( 78 this.videosSelection.disableForReuse()
65 debounceTime(400),
66 distinctUntilChanged()
67 )
68 .subscribe(search => {
69 this.search = search
70 this.reloadVideos()
71 })
72 } 79 }
73 80
74 onSearch (event: Event) { 81 enabledForReuse () {
75 const target = event.target as HTMLInputElement 82 this.videosSelection.enabledForReuse()
76 this.searchStream.next(target.value)
77 } 83 }
78 84
79 resetSearch () { 85 reloadData () {
80 const searchInput = document.getElementById('history-search') as HTMLInputElement 86 this.videosSelection.reloadVideos()
81 searchInput.value = ''
82 this.searchStream.next('')
83 } 87 }
84 88
85 ngOnDestroy () { 89 onSearch (search: string) {
86 super.ngOnDestroy() 90 this.search = search
91 this.reloadData()
87 } 92 }
88 93
89 getVideosObservable (page: number) { 94 getVideosObservable (page: number) {
@@ -129,7 +134,7 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD
129 () => { 134 () => {
130 this.notifier.success($localize`Videos history deleted`) 135 this.notifier.success($localize`Videos history deleted`)
131 136
132 this.reloadVideos() 137 this.reloadData()
133 }, 138 },
134 139
135 err => this.notifier.error(err.message) 140 err => this.notifier.error(err.message)
diff --git a/client/src/app/+my-library/my-library.component.scss b/client/src/app/+my-library/my-library.component.scss
index a5bb499b4..b32bc84e7 100644
--- a/client/src/app/+my-library/my-library.component.scss
+++ b/client/src/app/+my-library/my-library.component.scss
@@ -2,12 +2,12 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.row { 4.row {
5 @include sub-menu-h1;
6
5 flex-direction: column; 7 flex-direction: column;
6 width: 100%; 8 width: 100%;
7 9
8 & > my-top-menu-dropdown:nth-child(1) { 10 > my-top-menu-dropdown:nth-child(1) {
9 flex-grow: 1; 11 flex-grow: 1;
10 } 12 }
11
12 @include sub-menu-h1;
13} 13}
diff --git a/client/src/app/+my-library/my-library.module.ts b/client/src/app/+my-library/my-library.module.ts
index a1d706f0b..264ad03f7 100644
--- a/client/src/app/+my-library/my-library.module.ts
+++ b/client/src/app/+my-library/my-library.module.ts
@@ -26,7 +26,7 @@ import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-pl
26import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' 26import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component'
27import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' 27import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component'
28import { MyVideosComponent } from './my-videos/my-videos.component' 28import { MyVideosComponent } from './my-videos/my-videos.component'
29import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' 29import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
30 30
31@NgModule({ 31@NgModule({
32 imports: [ 32 imports: [
@@ -47,7 +47,7 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
47 SharedAbuseListModule, 47 SharedAbuseListModule,
48 SharedShareModal, 48 SharedShareModal,
49 SharedVideoLiveModule, 49 SharedVideoLiveModule,
50 SharedAccountAvatarModule 50 SharedActorImageModule
51 ], 51 ],
52 52
53 declarations: [ 53 declarations: [
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.html b/client/src/app/+my-library/my-ownership/my-ownership.component.html
index d0eff0521..4c02c78fc 100644
--- a/client/src/app/+my-library/my-ownership/my-ownership.component.html
+++ b/client/src/app/+my-library/my-ownership/my-ownership.component.html
@@ -37,7 +37,7 @@
37 <td> 37 <td>
38 <a [href]="videoChangeOwnership.initiatorAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 38 <a [href]="videoChangeOwnership.initiatorAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
39 <div class="chip two-lines"> 39 <div class="chip two-lines">
40 <my-account-avatar [account]="videoChangeOwnership.initiatorAccount"></my-account-avatar> 40 <my-actor-avatar [account]="videoChangeOwnership.initiatorAccount"></my-actor-avatar>
41 <div> 41 <div>
42 {{ videoChangeOwnership.initiatorAccount.displayName }} 42 {{ videoChangeOwnership.initiatorAccount.displayName }}
43 <span class="text-muted">{{ videoChangeOwnership.initiatorAccount.nameWithHost }}</span> 43 <span class="text-muted">{{ videoChangeOwnership.initiatorAccount.nameWithHost }}</span>
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.scss b/client/src/app/+my-library/my-ownership/my-ownership.component.scss
index 7cac9c9f3..dfc8fc99e 100644
--- a/client/src/app/+my-library/my-ownership/my-ownership.component.scss
+++ b/client/src/app/+my-library/my-ownership/my-ownership.component.scss
@@ -13,15 +13,15 @@
13 display: inline-flex; 13 display: inline-flex;
14 14
15 .video-table-video-image { 15 .video-table-video-image {
16 @include miniature-thumbnail;
17
18 $image-height: 45px; 16 $image-height: 45px;
19 17
18 @include miniature-thumbnail;
19
20 height: $image-height; 20 height: $image-height;
21 width: #{(16/9) * $image-height}; 21 width: #{(16/9) * $image-height};
22 margin-right: 0.5rem; 22 margin-right: 0.5rem;
23 border-radius: 2px; 23 border-radius: 2px;
24 border: none; 24 border: 0;
25 background: transparent; 25 background: transparent;
26 display: inline-flex; 26 display: inline-flex;
27 justify-content: center; 27 justify-content: center;
@@ -60,7 +60,7 @@
60 60
61 div .glyphicon { 61 div .glyphicon {
62 font-size: 80%; 62 font-size: 80%;
63 color: gray; 63 color: #808080;
64 margin-left: 0.1rem; 64 margin-left: 0.1rem;
65 } 65 }
66 66
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-ownership.component.ts
index a938023b4..aaf028474 100644
--- a/client/src/app/+my-library/my-ownership/my-ownership.component.ts
+++ b/client/src/app/+my-library/my-ownership/my-ownership.component.ts
@@ -48,18 +48,18 @@ export class MyOwnershipComponent extends RestTable implements OnInit {
48 } 48 }
49 49
50 accepted () { 50 accepted () {
51 this.loadData() 51 this.reloadData()
52 } 52 }
53 53
54 refuse (videoChangeOwnership: VideoChangeOwnership) { 54 refuse (videoChangeOwnership: VideoChangeOwnership) {
55 this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id) 55 this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id)
56 .subscribe( 56 .subscribe(
57 () => this.loadData(), 57 () => this.reloadData(),
58 err => this.notifier.error(err.message) 58 err => this.notifier.error(err.message)
59 ) 59 )
60 } 60 }
61 61
62 protected loadData () { 62 protected reloadData () {
63 return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort) 63 return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort)
64 .subscribe( 64 .subscribe(
65 resultList => { 65 resultList => {
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
index ff448ad87..f91cebacf 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
+++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
@@ -7,21 +7,14 @@
7</h1> 7</h1>
8 8
9<div class="video-subscriptions-header"> 9<div class="video-subscriptions-header">
10 <div class="has-feedback has-clear"> 10 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11 <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch"
12 (ngModelChange)="onSubscriptionsSearchChanged()" />
13 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
14 <span class="sr-only" i18n>Clear filters</span>
15 </div>
16</div> 11</div>
17 12
18<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div> 13<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div>
19 14
20<div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> 15<div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
21 <div *ngFor="let videoChannel of videoChannels" class="video-channel"> 16 <div *ngFor="let videoChannel of videoChannels" class="video-channel">
22 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"> 17 <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/video-channels', videoChannel.nameWithHost ]"></my-actor-avatar>
23 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
24 </a>
25 18
26 <div class="video-channel-info"> 19 <div class="video-channel-info">
27 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> 20 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page">
@@ -33,7 +26,8 @@
33 26
34 <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner"> 27 <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner">
35 <span i18n>Created by {{ videoChannel.ownerBy }}</span> 28 <span i18n>Created by {{ videoChannel.ownerBy }}</span>
36 <img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" /> 29
30 <my-actor-avatar [account]="videoChannel.ownerAccount" size="18"></my-actor-avatar>
37 </a> 31 </a>
38 </div> 32 </div>
39 33
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
index 3c1a4d2ad..6c1ddf716 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
+++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
@@ -8,8 +8,8 @@ input[type=text] {
8.video-channel { 8.video-channel {
9 @include row-blocks; 9 @include row-blocks;
10 10
11 img { 11 > my-actor-avatar {
12 @include channel-avatar(80px); 12 @include actor-avatar-size(80px);
13 13
14 margin-right: 10px; 14 margin-right: 10px;
15 } 15 }
@@ -40,13 +40,25 @@ input[type=text] {
40} 40}
41 41
42.actor-owner { 42.actor-owner {
43 @include actor-owner; 43 @include disable-default-a-behaviour;
44
45 font-size: 13px;
46 color: pvar(--mainForegroundColor);
44 47
45 margin-top: 0; 48 span:hover {
49 opacity: 0.8;
50 }
51
52 my-actor-avatar {
53 margin-left: 7px;
54 display: inline-block;
55 vertical-align: top;
56 }
46} 57}
47 58
48.video-subscriptions-header { 59.video-subscriptions-header {
49 margin-bottom: 30px; 60 margin-bottom: 30px;
61 display: flex;
50} 62}
51 63
52@media screen and (max-width: $small-view) { 64@media screen and (max-width: $small-view) {
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts
index 3b748eccf..1f4a931a0 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts
+++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts
@@ -1,6 +1,5 @@
1import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
2import { debounceTime } from 'rxjs/operators' 2import { Component } from '@angular/core'
3import { Component, OnInit } from '@angular/core'
4import { ComponentPagination, Notifier } from '@app/core' 3import { ComponentPagination, Notifier } from '@app/core'
5import { VideoChannel } from '@app/shared/shared-main' 4import { VideoChannel } from '@app/shared/shared-main'
6import { UserSubscriptionService } from '@app/shared/shared-user-subscription' 5import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
@@ -9,7 +8,7 @@ import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
9 templateUrl: './my-subscriptions.component.html', 8 templateUrl: './my-subscriptions.component.html',
10 styleUrls: [ './my-subscriptions.component.scss' ] 9 styleUrls: [ './my-subscriptions.component.scss' ]
11}) 10})
12export class MySubscriptionsComponent implements OnInit { 11export class MySubscriptionsComponent {
13 videoChannels: VideoChannel[] = [] 12 videoChannels: VideoChannel[] = []
14 13
15 pagination: ComponentPagination = { 14 pagination: ComponentPagination = {
@@ -20,34 +19,13 @@ export class MySubscriptionsComponent implements OnInit {
20 19
21 onDataSubject = new Subject<any[]>() 20 onDataSubject = new Subject<any[]>()
22 21
23 subscriptionsSearch: string 22 search: string
24 subscriptionsSearchChanged = new Subject<string>()
25 23
26 constructor ( 24 constructor (
27 private userSubscriptionService: UserSubscriptionService, 25 private userSubscriptionService: UserSubscriptionService,
28 private notifier: Notifier 26 private notifier: Notifier
29 ) {} 27 ) {}
30 28
31 ngOnInit () {
32 this.loadSubscriptions()
33
34 this.subscriptionsSearchChanged
35 .pipe(debounceTime(500))
36 .subscribe(() => {
37 this.pagination.currentPage = 1
38 this.loadSubscriptions(false)
39 })
40 }
41
42 resetSearch () {
43 this.subscriptionsSearch = ''
44 this.onSubscriptionsSearchChanged()
45 }
46
47 onSubscriptionsSearchChanged () {
48 this.subscriptionsSearchChanged.next()
49 }
50
51 onNearOfBottom () { 29 onNearOfBottom () {
52 // Last page 30 // Last page
53 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return 31 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
@@ -56,8 +34,13 @@ export class MySubscriptionsComponent implements OnInit {
56 this.loadSubscriptions() 34 this.loadSubscriptions()
57 } 35 }
58 36
37 onSearch (search: string) {
38 this.search = search
39 this.loadSubscriptions(false)
40 }
41
59 private loadSubscriptions (more = true) { 42 private loadSubscriptions (more = true) {
60 this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.subscriptionsSearch }) 43 this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.search })
61 .subscribe( 44 .subscribe(
62 res => { 45 res => {
63 this.videoChannels = more 46 this.videoChannels = more
diff --git a/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss b/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss
index a93c28028..c4b847c3d 100644
--- a/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss
+++ b/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss
@@ -6,7 +6,7 @@ pre {
6} 6}
7 7
8.video-import-error { 8.video-import-error {
9 color: red; 9 color: #ff0000;
10} 10}
11 11
12.badge { 12.badge {
diff --git a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts
index d6d7d7a1b..359535526 100644
--- a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts
+++ b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts
@@ -62,7 +62,7 @@ export class MyVideoImportsComponent extends RestTable implements OnInit {
62 return '/videos/update/' + video.uuid 62 return '/videos/update/' + video.uuid
63 } 63 }
64 64
65 protected loadData () { 65 protected reloadData () {
66 this.videoImportService.getMyVideoImports(this.pagination, this.sort) 66 this.videoImportService.getMyVideoImports(this.pagination, this.sort)
67 .subscribe( 67 .subscribe(
68 resultList => { 68 resultList => {
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss
index 0c68dedf6..67587a58a 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss
@@ -25,8 +25,8 @@
25} 25}
26 26
27.playlist-buttons { 27.playlist-buttons {
28 display:flex; 28 display: flex;
29 margin: 30px 0 10px 0; 29 margin: 30px 0 10px;
30 30
31 .share-button { 31 .share-button {
32 @include peertube-button; 32 @include peertube-button;
@@ -42,9 +42,10 @@
42.cdk-drag-preview { 42.cdk-drag-preview {
43 box-sizing: border-box; 43 box-sizing: border-box;
44 border-radius: 4px; 44 border-radius: 4px;
45 box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 45 box-shadow:
46 0 8px 10px 1px rgba(0, 0, 0, 0.14), 46 0 5px 5px -3px rgba(0, 0, 0, 0.2),
47 0 3px 14px 2px rgba(0, 0, 0, 0.12); 47 0 8px 10px 1px rgba(0, 0, 0, 0.14),
48 0 3px 14px 2px rgba(0, 0, 0, 0.12);
48} 49}
49 50
50.cdk-drag-placeholder { 51.cdk-drag-placeholder {
@@ -56,7 +57,7 @@
56} 57}
57 58
58.video:last-child { 59.video:last-child {
59 border: none; 60 border: 0;
60} 61}
61 62
62.videos.cdk-drop-list-dragging .video:not(.cdk-drag-placeholder) { 63.videos.cdk-drop-list-dragging .video:not(.cdk-drag-placeholder) {
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
index b88ea3db7..309afcf13 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
@@ -4,12 +4,7 @@
4</h1> 4</h1>
5 5
6<div class="video-playlists-header d-flex justify-content-between"> 6<div class="video-playlists-header d-flex justify-content-between">
7 <div class="has-feedback has-clear"> 7 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
8 <input type="text" placeholder="Search your playlists" i18n-placeholder [(ngModel)]="videoPlaylistsSearch"
9 (ngModelChange)="onVideoPlaylistSearchChanged()" />
10 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
11 <span class="sr-only" i18n>Clear filters</span>
12 </div>
13 8
14 <a class="create-button" routerLink="create"> 9 <a class="create-button" routerLink="create">
15 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 10 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts
index f6d394923..d90102693 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts
@@ -1,7 +1,7 @@
1import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
2import { debounceTime, mergeMap } from 'rxjs/operators' 2import { mergeMap } from 'rxjs/operators'
3import { Component, OnInit } from '@angular/core' 3import { Component } from '@angular/core'
4import { AuthService, ComponentPagination, ConfirmService, Notifier, User } from '@app/core' 4import { AuthService, ComponentPagination, ConfirmService, Notifier } from '@app/core'
5import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 5import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6import { VideoPlaylistType } from '@shared/models' 6import { VideoPlaylistType } from '@shared/models'
7 7
@@ -9,10 +9,8 @@ import { VideoPlaylistType } from '@shared/models'
9 templateUrl: './my-video-playlists.component.html', 9 templateUrl: './my-video-playlists.component.html',
10 styleUrls: [ './my-video-playlists.component.scss' ] 10 styleUrls: [ './my-video-playlists.component.scss' ]
11}) 11})
12export class MyVideoPlaylistsComponent implements OnInit { 12export class MyVideoPlaylistsComponent {
13 videoPlaylistsSearch: string
14 videoPlaylists: VideoPlaylist[] = [] 13 videoPlaylists: VideoPlaylist[] = []
15 videoPlaylistSearchChanged = new Subject<string>()
16 14
17 pagination: ComponentPagination = { 15 pagination: ComponentPagination = {
18 currentPage: 1, 16 currentPage: 1,
@@ -22,27 +20,14 @@ export class MyVideoPlaylistsComponent implements OnInit {
22 20
23 onDataSubject = new Subject<any[]>() 21 onDataSubject = new Subject<any[]>()
24 22
25 private user: User 23 search: string
26 24
27 constructor ( 25 constructor (
28 private authService: AuthService, 26 private authService: AuthService,
29 private notifier: Notifier, 27 private notifier: Notifier,
30 private confirmService: ConfirmService, 28 private confirmService: ConfirmService,
31 private videoPlaylistService: VideoPlaylistService 29 private videoPlaylistService: VideoPlaylistService
32 ) {} 30 ) {}
33
34 ngOnInit () {
35 this.user = this.authService.getUser()
36
37 this.loadVideoPlaylists()
38
39 this.videoPlaylistSearchChanged
40 .pipe(
41 debounceTime(500))
42 .subscribe(() => {
43 this.loadVideoPlaylists(true)
44 })
45 }
46 31
47 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { 32 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) {
48 const res = await this.confirmService.confirm( 33 const res = await this.confirmService.confirm(
@@ -76,22 +61,20 @@ export class MyVideoPlaylistsComponent implements OnInit {
76 this.loadVideoPlaylists() 61 this.loadVideoPlaylists()
77 } 62 }
78 63
79 resetSearch () { 64 onSearch (search: string) {
80 this.videoPlaylistsSearch = '' 65 this.search = search
81 this.onVideoPlaylistSearchChanged() 66 this.loadVideoPlaylists(true)
82 }
83
84 onVideoPlaylistSearchChanged () {
85 this.videoPlaylistSearchChanged.next()
86 } 67 }
87 68
88 private loadVideoPlaylists (reset = false) { 69 private loadVideoPlaylists (reset = false) {
89 this.authService.userInformationLoaded 70 this.authService.userInformationLoaded
90 .pipe(mergeMap(() => { 71 .pipe(mergeMap(() => {
91 return this.videoPlaylistService.listAccountPlaylists(this.user.account, this.pagination, '-updatedAt', this.videoPlaylistsSearch) 72 const user = this.authService.getUser()
92 })) 73
93 .subscribe(res => { 74 return this.videoPlaylistService.listAccountPlaylists(user.account, this.pagination, '-updatedAt', this.search)
75 })).subscribe(res => {
94 if (reset) this.videoPlaylists = [] 76 if (reset) this.videoPlaylists = []
77
95 this.videoPlaylists = this.videoPlaylists.concat(res.data) 78 this.videoPlaylists = this.videoPlaylists.concat(res.data)
96 this.pagination.totalItems = res.total 79 this.pagination.totalItems = res.total
97 80
diff --git a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss
index a79fec179..16187bc4a 100644
--- a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss
+++ b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss
@@ -7,4 +7,4 @@ p-autocomplete {
7 7
8.form-group { 8.form-group {
9 margin: 20px 0; 9 margin: 20px 0;
10} \ No newline at end of file 10}
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html
index e9f436378..8d8b482ad 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.html
+++ b/client/src/app/+my-library/my-videos/my-videos.component.html
@@ -19,12 +19,7 @@
19</h1> 19</h1>
20 20
21<div class="videos-header d-flex justify-content-between"> 21<div class="videos-header d-flex justify-content-between">
22 <div class="has-feedback has-clear"> 22 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
23 <input type="text" placeholder="Search your videos" i18n-placeholder [(ngModel)]="videosSearch"
24 (ngModelChange)="onVideosSearchChanged()" />
25 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
26 <span class="sr-only" i18n>Clear filters</span>
27 </div>
28 23
29 <div class="peertube-select-container peertube-select-button"> 24 <div class="peertube-select-container peertube-select-button">
30 <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> 25 <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control">
@@ -46,6 +41,7 @@
46 [titlePage]="titlePage" 41 [titlePage]="titlePage"
47 [getVideosObservableFunction]="getVideosObservableFunction" 42 [getVideosObservableFunction]="getVideosObservableFunction"
48 [user]="user" 43 [user]="user"
44 [loadOnInit]="false"
49 #videosSelection 45 #videosSelection
50> 46>
51 <ng-template ptTemplate="globalButtons"> 47 <ng-template ptTemplate="globalButtons">
@@ -64,6 +60,5 @@
64 </ng-template> 60 </ng-template>
65</my-videos-selection> 61</my-videos-selection>
66 62
67
68<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership> 63<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership>
69<my-live-stream-information #liveStreamInformationModal></my-live-stream-information> 64<my-live-stream-information #liveStreamInformationModal></my-live-stream-information>
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.scss b/client/src/app/+my-library/my-videos/my-videos.component.scss
index aaf21126b..57623c36f 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.scss
+++ b/client/src/app/+my-library/my-videos/my-videos.component.scss
@@ -26,12 +26,12 @@ h1 {
26} 26}
27 27
28.action-button-delete-selection { 28.action-button-delete-selection {
29 display: inline-block;
30
31 @include peertube-button; 29 @include peertube-button;
32 @include orange-button; 30 @include orange-button;
33 @include button-with-icon(21px); 31 @include button-with-icon(21px);
34 32
33 display: inline-block;
34
35 my-global-icon { 35 my-global-icon {
36 @include apply-svg-color(#fff); 36 @include apply-svg-color(#fff);
37 } 37 }
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts
index 356e158d6..1e4a4406d 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.ts
+++ b/client/src/app/+my-library/my-videos/my-videos.component.ts
@@ -1,10 +1,11 @@
1import { concat, Observable, Subject } from 'rxjs' 1import { concat, Observable } from 'rxjs'
2import { debounceTime, tap, toArray } from 'rxjs/operators' 2import { tap, toArray } from 'rxjs/operators'
3import { Component, OnInit, ViewChild } from '@angular/core' 3import { Component, OnInit, ViewChild } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' 5import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core'
6import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 6import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
7import { immutableAssign } from '@app/helpers' 7import { immutableAssign } from '@app/helpers'
8import { AdvancedInputFilter } from '@app/shared/shared-forms'
8import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 9import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
9import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' 10import { LiveStreamInformationComponent } from '@app/shared/shared-video-live'
10import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' 11import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
@@ -40,13 +41,21 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
40 videoActions: DropdownAction<{ video: Video }>[] = [] 41 videoActions: DropdownAction<{ video: Video }>[] = []
41 42
42 videos: Video[] = [] 43 videos: Video[] = []
43 videosSearch: string
44 videosSearchChanged = new Subject<string>()
45 getVideosObservableFunction = this.getVideosObservable.bind(this) 44 getVideosObservableFunction = this.getVideosObservable.bind(this)
45
46 sort: VideoSortField = '-publishedAt' 46 sort: VideoSortField = '-publishedAt'
47 47
48 user: User 48 user: User
49 49
50 inputFilters: AdvancedInputFilter[] = [
51 {
52 queryParams: { 'search': 'isLive:true' },
53 label: $localize`Only live videos`
54 }
55 ]
56
57 private search: string
58
50 constructor ( 59 constructor (
51 protected router: Router, 60 protected router: Router,
52 protected serverService: ServerService, 61 protected serverService: ServerService,
@@ -64,21 +73,15 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
64 this.buildActions() 73 this.buildActions()
65 74
66 this.user = this.authService.getUser() 75 this.user = this.authService.getUser()
67
68 this.videosSearchChanged
69 .pipe(debounceTime(500))
70 .subscribe(() => {
71 this.videosSelection.reloadVideos()
72 })
73 } 76 }
74 77
75 resetSearch () { 78 onSearch (search: string) {
76 this.videosSearch = '' 79 this.search = search
77 this.onVideosSearchChanged() 80 this.reloadData()
78 } 81 }
79 82
80 onVideosSearchChanged () { 83 reloadData () {
81 this.videosSearchChanged.next() 84 this.videosSelection.reloadVideos()
82 } 85 }
83 86
84 onChangeSortColumn () { 87 onChangeSortColumn () {
@@ -96,7 +99,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
96 getVideosObservable (page: number) { 99 getVideosObservable (page: number) {
97 const newPagination = immutableAssign(this.pagination, { currentPage: page }) 100 const newPagination = immutableAssign(this.pagination, { currentPage: page })
98 101
99 return this.videoService.getMyVideos(newPagination, this.sort, this.videosSearch) 102 return this.videoService.getMyVideos(newPagination, this.sort, this.search)
100 .pipe( 103 .pipe(
101 tap(res => this.pagination.totalItems = res.total) 104 tap(res => this.pagination.totalItems = res.total)
102 ) 105 )
diff --git a/client/src/app/+search/search-filters.component.scss b/client/src/app/+search/search-filters.component.scss
index 68ac6d021..cfb7a1d98 100644
--- a/client/src/app/+search/search-filters.component.scss
+++ b/client/src/app/+search/search-filters.component.scss
@@ -46,7 +46,7 @@ input[type=submit] {
46 46
47 font-weight: $font-semibold; 47 font-weight: $font-semibold;
48 display: inline-block; 48 display: inline-block;
49 padding: 0 10px 0 10px; 49 padding: 0 10px;
50 white-space: nowrap; 50 white-space: nowrap;
51 background: transparent; 51 background: transparent;
52 52
diff --git a/client/src/app/+search/search.component.html b/client/src/app/+search/search.component.html
index 65d4b6ecd..130be75fc 100644
--- a/client/src/app/+search/search.component.html
+++ b/client/src/app/+search/search.component.html
@@ -33,20 +33,15 @@
33 33
34 <ng-container *ngFor="let result of results"> 34 <ng-container *ngFor="let result of results">
35 <div *ngIf="isVideoChannel(result)" class="entry video-channel"> 35 <div *ngIf="isVideoChannel(result)" class="entry video-channel">
36 <a class="link-avatar" *ngIf="!isExternalChannelUrl()" [routerLink]="getChannelUrl(result)">
37 <img [src]="result.avatarUrl" alt="Avatar" />
38 </a>
39 36
40 <a class="link-avatar" *ngIf="isExternalChannelUrl()" [href]="getChannelUrl(result)" target="_blank"> 37 <my-actor-avatar [channel]="result" [internalHref]="getInternalChannelUrl(result)" [href]="getExternalChannelUrl(result)"></my-actor-avatar>
41 <img [src]="result.avatarUrl" alt="Avatar" />
42 </a>
43 38
44 <div class="video-channel-info"> 39 <div class="video-channel-info">
45 <a *ngIf="!isExternalChannelUrl()" [routerLink]="getChannelUrl(result)" class="video-channel-names"> 40 <a *ngIf="!isExternalChannelUrl()" [routerLink]="getInternalChannelUrl(result)" class="video-channel-names">
46 <ng-container *ngTemplateOutlet="aContent"></ng-container> 41 <ng-container *ngTemplateOutlet="aContent"></ng-container>
47 </a> 42 </a>
48 43
49 <a *ngIf="isExternalChannelUrl()" [href]="getChannelUrl(result)" target="_blank" class="video-channel-names"> 44 <a *ngIf="isExternalChannelUrl()" [href]="getExternalChannelUrl(result)" target="_blank" class="video-channel-names">
50 <ng-container *ngTemplateOutlet="aContent"></ng-container> 45 <ng-container *ngTemplateOutlet="aContent"></ng-container>
51 </a> 46 </a>
52 47
diff --git a/client/src/app/+search/search.component.scss b/client/src/app/+search/search.component.scss
index 91c8272d7..a8002ba88 100644
--- a/client/src/app/+search/search.component.scss
+++ b/client/src/app/+search/search.component.scss
@@ -5,7 +5,7 @@
5 $image-size: min(130px, $video-img-width); 5 $image-size: min(130px, $video-img-width);
6 $margin-size: ($video-img-width - $image-size) / 2; // So we have the same width than the video miniature 6 $margin-size: ($video-img-width - $image-size) / 2; // So we have the same width than the video miniature
7 7
8 @include channel-avatar($image-size); 8 @include actor-avatar-size($image-size);
9 9
10 margin: 0 $margin-size 0 $margin-size; 10 margin: 0 $margin-size 0 $margin-size;
11} 11}
@@ -53,10 +53,8 @@
53 max-width: 800px; 53 max-width: 800px;
54} 54}
55 55
56.video-channel { 56.video-channel my-actor-avatar {
57 img { 57 @include build-channel-img-size($video-thumbnail-width);
58 @include build-channel-img-size($video-thumbnail-width);
59 }
60} 58}
61 59
62.video-channel-info { 60.video-channel-info {
@@ -92,14 +90,12 @@
92 grid-template-columns: auto 1fr; 90 grid-template-columns: auto 1fr;
93 grid-template-rows: auto auto; 91 grid-template-rows: auto auto;
94 92
95 .link-avatar { 93 my-actor-avatar {
94 @include build-channel-img-size($video-thumbnail-medium-width);
95
96 grid-column: 1; 96 grid-column: 1;
97 grid-row: 1 / -1; 97 grid-row: 1 / -1;
98 } 98 }
99
100 img {
101 @include build-channel-img-size($video-thumbnail-medium-width);
102 }
103 } 99 }
104 100
105 .video-channel-info { 101 .video-channel-info {
@@ -115,7 +111,7 @@
115} 111}
116 112
117@include on-mobile-main-col { 113@include on-mobile-main-col {
118 .video-channel img { 114 .video-channel my-actor-avatar {
119 @include build-channel-img-size($video-thumbnail-small-width); 115 @include build-channel-img-size($video-thumbnail-small-width);
120 } 116 }
121} 117}
diff --git a/client/src/app/+search/search.component.ts b/client/src/app/+search/search.component.ts
index 2be952e16..ecede19a3 100644
--- a/client/src/app/+search/search.component.ts
+++ b/client/src/app/+search/search.component.ts
@@ -132,10 +132,6 @@ export class SearchComponent implements OnInit, OnDestroy {
132 return 'internal' 132 return 'internal'
133 } 133 }
134 134
135 isExternalChannelUrl () {
136 return this.getVideoLinkType() === 'external'
137 }
138
139 search () { 135 search () {
140 forkJoin([ 136 forkJoin([
141 this.getVideosObs(), 137 this.getVideosObs(),
@@ -200,17 +196,33 @@ export class SearchComponent implements OnInit, OnDestroy {
200 this.results = this.results.filter(r => !this.isVideo(r) || r.id !== video.id) 196 this.results = this.results.filter(r => !this.isVideo(r) || r.id !== video.id)
201 } 197 }
202 198
203 getChannelUrl (channel: VideoChannel) { 199 isExternalChannelUrl () {
200 return this.getVideoLinkType() === 'external'
201 }
202
203 getExternalChannelUrl (channel: VideoChannel) {
204 // Same algorithm than videos 204 // Same algorithm than videos
205 if (this.getVideoLinkType() === 'external') { 205 if (this.getVideoLinkType() === 'external') {
206 return channel.url 206 return channel.url
207 } 207 }
208 208
209 if (this.getVideoLinkType() === 'internal') { 209 // lazy-load or internal
210 return undefined
211 }
212
213 getInternalChannelUrl (channel: VideoChannel) {
214 const linkType = this.getVideoLinkType()
215
216 if (linkType === 'internal') {
210 return [ '/video-channels', channel.nameWithHost ] 217 return [ '/video-channels', channel.nameWithHost ]
211 } 218 }
212 219
213 return [ '/search/lazy-load-channel', { url: channel.url } ] 220 if (linkType === 'lazy-load') {
221 return [ '/search/lazy-load-channel', { url: channel.url } ]
222 }
223
224 // external
225 return undefined
214 } 226 }
215 227
216 hideActions () { 228 hideActions () {
diff --git a/client/src/app/+search/search.module.ts b/client/src/app/+search/search.module.ts
index e85ae07d0..390833abc 100644
--- a/client/src/app/+search/search.module.ts
+++ b/client/src/app/+search/search.module.ts
@@ -1,4 +1,5 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { SharedActorImageModule } from '@app/shared/shared-actor-image/shared-actor-image.module'
2import { SharedFormModule } from '@app/shared/shared-forms' 3import { SharedFormModule } from '@app/shared/shared-forms'
3import { SharedMainModule } from '@app/shared/shared-main' 4import { SharedMainModule } from '@app/shared/shared-main'
4import { SharedSearchModule } from '@app/shared/shared-search' 5import { SharedSearchModule } from '@app/shared/shared-search'
@@ -18,6 +19,7 @@ import { VideoLazyLoadResolver } from './video-lazy-load.resolver'
18 SharedMainModule, 19 SharedMainModule,
19 SharedSearchModule, 20 SharedSearchModule,
20 SharedFormModule, 21 SharedFormModule,
22 SharedActorImageModule,
21 SharedUserSubscriptionModule, 23 SharedUserSubscriptionModule,
22 SharedVideoMiniatureModule 24 SharedVideoMiniatureModule
23 ], 25 ],
diff --git a/client/src/app/+signup/+register/register.component.scss b/client/src/app/+signup/+register/register.component.scss
index 16ba9e2c0..f6a846ffa 100644
--- a/client/src/app/+signup/+register/register.component.scss
+++ b/client/src/app/+signup/+register/register.component.scss
@@ -84,7 +84,7 @@ button {
84 border-color: pvar(--mainColor) transparent transparent transparent; 84 border-color: pvar(--mainColor) transparent transparent transparent;
85 } 85 }
86 86
87 & + div { 87 + div {
88 font-size: 15px; 88 font-size: 15px;
89 } 89 }
90 } 90 }
diff --git a/client/src/app/+signup/shared/signup-success.component.scss b/client/src/app/+signup/shared/signup-success.component.scss
index fbc27c8bc..b302366e2 100644
--- a/client/src/app/+signup/shared/signup-success.component.scss
+++ b/client/src/app/+signup/shared/signup-success.component.scss
@@ -9,19 +9,16 @@ svg {
9 stroke-dashoffset: 0; 9 stroke-dashoffset: 0;
10 10
11 &.circle { 11 &.circle {
12 -webkit-animation: dash .9s ease-in-out;
13 animation: dash .9s ease-in-out; 12 animation: dash .9s ease-in-out;
14 } 13 }
15 14
16 &.line { 15 &.line {
17 stroke-dashoffset: 1000; 16 stroke-dashoffset: 1000;
18 -webkit-animation: dash .9s .35s ease-in-out forwards;
19 animation: dash .9s .35s ease-in-out forwards; 17 animation: dash .9s .35s ease-in-out forwards;
20 } 18 }
21 19
22 &.check { 20 &.check {
23 stroke-dashoffset: -100; 21 stroke-dashoffset: -100;
24 -webkit-animation: dash-check .9s .35s ease-in-out forwards;
25 animation: dash-check .9s .35s ease-in-out forwards; 22 animation: dash-check .9s .35s ease-in-out forwards;
26 } 23 }
27} 24}
@@ -38,16 +35,6 @@ svg {
38 text-align: center; 35 text-align: center;
39} 36}
40 37
41
42@-webkit-keyframes dash {
43 0% {
44 stroke-dashoffset: 1000;
45 }
46 100% {
47 stroke-dashoffset: 0;
48 }
49}
50
51@keyframes dash { 38@keyframes dash {
52 0% { 39 0% {
53 stroke-dashoffset: 1000; 40 stroke-dashoffset: 1000;
@@ -57,15 +44,6 @@ svg {
57 } 44 }
58} 45}
59 46
60@-webkit-keyframes dash-check {
61 0% {
62 stroke-dashoffset: -100;
63 }
64 100% {
65 stroke-dashoffset: 900;
66 }
67}
68
69@keyframes dash-check { 47@keyframes dash-check {
70 0% { 48 0% {
71 stroke-dashoffset: -100; 49 stroke-dashoffset: -100;
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index 9308d5bb6..b4d81fe39 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -6,16 +6,16 @@
6 <div class="channel-info"> 6 <div class="channel-info">
7 7
8 <ng-template #buttonsTemplate> 8 <ng-template #buttonsTemplate>
9 <a *ngIf="isManageable()" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="peertube-button-link orange-button" i18n> 9 <a *ngIf="isManageable()" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="peertube-button-link orange-button" i18n>
10 Manage channel 10 Manage channel
11 </a> 11 </a>
12 12
13 <my-subscribe-button *ngIf="!isManageable()" #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button> 13 <my-subscribe-button *ngIf="!isManageable()" #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button>
14 14
15 <button *ngIf="videoChannel.support" (click)="showSupportModal()" class="support-button peertube-button orange-button-inverted"> 15 <button *ngIf="videoChannel.support" (click)="showSupportModal()" class="support-button peertube-button orange-button-inverted">
16 <my-global-icon iconName="support" aria-hidden="true"></my-global-icon> 16 <my-global-icon iconName="support" aria-hidden="true"></my-global-icon>
17 <span class="icon-text" i18n>Support</span> 17 <span class="icon-text" i18n>Support</span>
18 </button> 18 </button>
19 </ng-template> 19 </ng-template>
20 20
21 <ng-template #ownerTemplate> 21 <ng-template #ownerTemplate>
@@ -23,7 +23,7 @@
23 <div class="section-label" i18n>OWNER ACCOUNT</div> 23 <div class="section-label" i18n>OWNER ACCOUNT</div>
24 24
25 <div class="avatar-row"> 25 <div class="avatar-row">
26 <my-account-avatar [account]="videoChannel.ownerAccount" [internalHref]="getAccountUrl()" size="120"></my-account-avatar> 26 <my-actor-avatar class="account-avatar" [account]="videoChannel.ownerAccount" [internalHref]="getAccountUrl()"></my-actor-avatar>
27 27
28 <div class="actor-info"> 28 <div class="actor-info">
29 <h4> 29 <h4>
@@ -49,7 +49,7 @@
49 </ng-template> 49 </ng-template>
50 50
51 <div class="channel-avatar-row"> 51 <div class="channel-avatar-row">
52 <img class="channel-avatar" [src]="videoChannel.avatarUrl" alt="Avatar" /> 52 <my-actor-avatar class="main-avatar" [channel]="videoChannel"></my-actor-avatar>
53 53
54 <div> 54 <div>
55 <div class="section-label" i18n>VIDEO CHANNEL</div> 55 <div class="section-label" i18n>VIDEO CHANNEL</div>
diff --git a/client/src/app/+video-channels/video-channels.component.scss b/client/src/app/+video-channels/video-channels.component.scss
index e946707ef..470f64878 100644
--- a/client/src/app/+video-channels/video-channels.component.scss
+++ b/client/src/app/+video-channels/video-channels.component.scss
@@ -107,8 +107,8 @@
107 display: flex; 107 display: flex;
108 margin-bottom: 15px; 108 margin-bottom: 15px;
109 109
110 img { 110 .account-avatar {
111 @include avatar(48px); 111 @include actor-avatar-size(48px);
112 } 112 }
113 113
114 .actor-info { 114 .actor-info {
@@ -131,10 +131,10 @@
131 } 131 }
132 132
133 .owner-description { 133 .owner-description {
134 @include fade-text(120px, pvar(--mainBackgroundColor));
135
134 max-height: 140px; 136 max-height: 140px;
135 word-break: break-word; 137 word-break: break-word;
136
137 @include fade-text(120px, pvar(--mainBackgroundColor));
138 } 138 }
139} 139}
140 140
@@ -150,7 +150,7 @@
150} 150}
151 151
152.copy-button { 152.copy-button {
153 border: none; 153 border: 0;
154} 154}
155 155
156@media screen and (max-width: 1400px) { 156@media screen and (max-width: 1400px) {
@@ -178,9 +178,9 @@
178 } 178 }
179 179
180 .channel-description:not(.expanded) { 180 .channel-description:not(.expanded) {
181 max-height: 70px;
182
183 @include fade-text(30px, pvar(--channelBackgroundColor)); 181 @include fade-text(30px, pvar(--channelBackgroundColor));
182
183 max-height: 70px;
184 } 184 }
185 185
186 .show-more { 186 .show-more {
@@ -220,10 +220,10 @@
220 } 220 }
221 221
222 .owner-description { 222 .owner-description {
223 @include fade-text(30px, pvar(--mainBackgroundColor));
224
223 grid-column: 2; 225 grid-column: 2;
224 max-height: 70px; 226 max-height: 70px;
225
226 @include fade-text(30px, pvar(--mainBackgroundColor));
227 } 227 }
228 228
229 .view-account { 229 .view-account {
@@ -289,8 +289,8 @@
289 margin-top: -5px; 289 margin-top: -5px;
290 } 290 }
291 291
292 img { 292 .account-avatar {
293 @include channel-avatar(64px); 293 @include actor-avatar-size(64px);
294 294
295 margin: -30px 0 0 15px; 295 margin: -30px 0 0 15px;
296 } 296 }
diff --git a/client/src/app/+video-channels/video-channels.module.ts b/client/src/app/+video-channels/video-channels.module.ts
index 2e387f401..35c39cc2e 100644
--- a/client/src/app/+video-channels/video-channels.module.ts
+++ b/client/src/app/+video-channels/video-channels.module.ts
@@ -10,7 +10,7 @@ import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-
10import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' 10import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
11import { VideoChannelsRoutingModule } from './video-channels-routing.module' 11import { VideoChannelsRoutingModule } from './video-channels-routing.module'
12import { VideoChannelsComponent } from './video-channels.component' 12import { VideoChannelsComponent } from './video-channels.component'
13import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' 13import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
14 14
15@NgModule({ 15@NgModule({
16 imports: [ 16 imports: [
@@ -23,7 +23,7 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
23 SharedUserSubscriptionModule, 23 SharedUserSubscriptionModule,
24 SharedGlobalIconModule, 24 SharedGlobalIconModule,
25 SharedSupportModal, 25 SharedSupportModal,
26 SharedAccountAvatarModule 26 SharedActorImageModule
27 ], 27 ],
28 28
29 declarations: [ 29 declarations: [
diff --git a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss
index 0958b5f80..a85cf444c 100644
--- a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss
+++ b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss
@@ -16,6 +16,6 @@ label {
16} 16}
17 17
18.warning-replace-caption { 18.warning-replace-caption {
19 color: red; 19 color: #ff0000;
20 margin-top: 10px; 20 margin-top: 10px;
21} \ No newline at end of file 21}
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index 6fe52af67..094b4d3b3 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -132,7 +132,7 @@
132 </ng-template> 132 </ng-template>
133 133
134 <ng-template ptTemplate="help"> 134 <ng-template ptTemplate="help">
135 <ng-container i18n>Some instances do not list videos containing mature or explicit content by default.</ng-container> 135 <ng-container i18n>Some instances hide videos containing mature or explicit content by default.</ng-container>
136 </ng-template> 136 </ng-template>
137 </my-peertube-checkbox> 137 </my-peertube-checkbox>
138 138
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
index 0b70b0270..bc32d7964 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
@@ -150,7 +150,7 @@ p-calendar {
150 @include media-breakpoint-up(md) { 150 @include media-breakpoint-up(md) {
151 @include make-col(7); 151 @include make-col(7);
152 152
153 & + .col-video-edit { 153 + .col-video-edit {
154 @include make-col(5); 154 @include make-col(5);
155 } 155 }
156 } 156 }
@@ -158,7 +158,7 @@ p-calendar {
158 @include media-breakpoint-up(xl) { 158 @include media-breakpoint-up(xl) {
159 @include make-col(8); 159 @include make-col(8);
160 160
161 & + .col-video-edit { 161 + .col-video-edit {
162 @include make-col(4); 162 @include make-col(4);
163 } 163 }
164 } 164 }
@@ -169,7 +169,7 @@ p-calendar {
169 @include media-breakpoint-up(md) { 169 @include media-breakpoint-up(md) {
170 @include make-col(8); 170 @include make-col(8);
171 171
172 & + .col-video-edit { 172 + .col-video-edit {
173 @include make-col(4); 173 @include make-col(4);
174 } 174 }
175 } 175 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-send.scss b/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
index 17c5f63e9..dc9153b2b 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
@@ -6,7 +6,7 @@ $width-size: 190px;
6.alert.alert-danger { 6.alert.alert-danger {
7 text-align: center; 7 text-align: center;
8 8
9 & > div { 9 > div {
10 font-weight: $font-semibold; 10 font-weight: $font-semibold;
11 } 11 }
12} 12}
@@ -17,10 +17,10 @@ $width-size: 190px;
17 align-items: center; 17 align-items: center;
18 18
19 .upload-icon { 19 .upload-icon {
20 @include apply-svg-color(#C6C6C6);
21
20 width: 90px; 22 width: 90px;
21 margin-bottom: 25px; 23 margin-bottom: 25px;
22
23 @include apply-svg-color(#C6C6C6);
24 } 24 }
25 25
26 .peertube-select-container { 26 .peertube-select-container {
diff --git a/client/src/app/+videos/+video-edit/video-add.component.scss b/client/src/app/+videos/+video-edit/video-add.component.scss
index 1ebee946b..35bca24d0 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.scss
+++ b/client/src/app/+videos/+video-edit/video-add.component.scss
@@ -44,7 +44,7 @@ $nav-link-height: 40px;
44 44
45::ng-deep .video-add-nav { 45::ng-deep .video-add-nav {
46 border-bottom: $border-width $border-type $border-color; 46 border-bottom: $border-width $border-type $border-color;
47 margin: 20px 0 0 0 !important; 47 margin: 20px 0 0 !important;
48 48
49 &.hide-nav { 49 &.hide-nav {
50 display: none !important; 50 display: none !important;
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
index 7bd9b7c90..42adfed8d 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
@@ -1,6 +1,6 @@
1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()"> 1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
2 <div class="avatar-and-textarea"> 2 <div class="avatar-and-textarea">
3 <my-account-avatar [account]="user?.account" size="25"></my-account-avatar> 3 <my-actor-avatar [account]="user?.account" size="25"></my-actor-avatar>
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize 6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
index 1aa9255c2..7743bd41d 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
@@ -13,8 +13,7 @@ form {
13 display: flex; 13 display: flex;
14 margin-bottom: 10px; 14 margin-bottom: 10px;
15 15
16 my-account-avatar { 16 my-actor-avatar {
17 vertical-align: top;
18 margin-right: 10px; 17 margin-right: 10px;
19 } 18 }
20 19
@@ -32,7 +31,7 @@ form {
32 padding-right: $markdown-icon-width + 15px !important; 31 padding-right: $markdown-icon-width + 15px !important;
33 32
34 @media screen and (max-width: 600px) { 33 @media screen and (max-width: 600px) {
35 padding-right: $markdown-icon-width + 19px !important; 34 padding-right: $markdown-icon-width + 19px !important;
36 } 35 }
37 36
38 &:focus::placeholder { 37 &:focus::placeholder {
@@ -58,7 +57,9 @@ form {
58 } 57 }
59 } 58 }
60 59
61 &:focus, &:active, &:hover { 60 &:focus,
61 &:active,
62 &:hover {
62 my-global-icon svg { 63 my-global-icon svg {
63 background-color: #C6C6C6; 64 background-color: #C6C6C6;
64 color: pvar(--mainBackgroundColor); 65 color: pvar(--mainBackgroundColor);
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
index 2b0739261..d7ba40ef6 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
@@ -1,12 +1,10 @@
1<div *ngIf="isCommentDisplayed()" class="root-comment"> 1<div *ngIf="isCommentDisplayed()" class="root-comment" [ngClass]="{ 'is-child': isChild() }">
2 <div class="left"> 2 <div class="left">
3 <my-account-avatar *ngIf="!comment.isDeleted" [href]="comment.account.url" [account]="comment.account"></my-account-avatar> 3 <my-actor-avatar *ngIf="!comment.isDeleted" [href]="comment.account.url" [account]="comment.account"></my-actor-avatar>
4 <div class="vertical-border"></div> 4 <div class="vertical-border"></div>
5 </div> 5 </div>
6 6
7 <div class="right" [ngClass]="{ 'mb-3': firstInThread }"> 7 <div class="right" [ngClass]="{ 'mb-3': firstInThread }">
8 <span *ngIf="comment.isDeleted" class="comment-avatar"></span>
9
10 <div class="comment"> 8 <div class="comment">
11 <ng-container *ngIf="!comment.isDeleted"> 9 <ng-container *ngIf="!comment.isDeleted">
12 <div *ngIf="highlightedComment === true" class="highlighted-comment" i18n>Highlighted comment</div> 10 <div *ngIf="highlightedComment === true" class="highlighted-comment" i18n>Highlighted comment</div>
@@ -68,7 +66,7 @@
68 [textValue]="redraftValue" 66 [textValue]="redraftValue"
69 ></my-video-comment-add> 67 ></my-video-comment-add>
70 68
71 <div *ngIf="commentTree" class="children"> 69 <div *ngIf="commentTree">
72 <div *ngFor="let commentChild of commentTree.children"> 70 <div *ngFor="let commentChild of commentTree.children">
73 <my-video-comment 71 <my-video-comment
74 [comment]="commentChild.comment" 72 [comment]="commentChild.comment"
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
index cf33a5b0e..a4d2e237c 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
@@ -24,6 +24,10 @@
24 } 24 }
25} 25}
26 26
27my-actor-avatar {
28 @include actor-avatar-size(36px);
29}
30
27.comment { 31.comment {
28 flex-grow: 1; 32 flex-grow: 1;
29 // Fix word-wrap with flex 33 // Fix word-wrap with flex
@@ -58,7 +62,7 @@
58 display: inline-flex; 62 display: inline-flex;
59 padding-right: 6px; 63 padding-right: 6px;
60 padding-left: 6px; 64 padding-left: 6px;
61 color: white !important; 65 color: #fff !important;
62} 66}
63 67
64.comment-account { 68.comment-account {
@@ -129,7 +133,10 @@
129 cursor: pointer; 133 cursor: pointer;
130 margin-right: 10px; 134 margin-right: 10px;
131 135
132 &:hover, &:active, &:focus, &:focus-visible { 136 &:hover,
137 &:active,
138 &:focus,
139 &:focus-visible {
133 color: pvar(--mainForegroundColor); 140 color: pvar(--mainForegroundColor);
134 } 141 }
135 } 142 }
@@ -148,10 +155,10 @@ my-video-comment-add {
148 } 155 }
149} 156}
150 157
151.children { 158.is-child {
152 // Reduce avatars size for replies 159 // Reduce avatars size for replies
153 .comment-avatar { 160 my-actor-avatar {
154 @include avatar(25px); 161 @include actor-avatar-size(25px);
155 } 162 }
156 163
157 .left { 164 .left {
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
index dd3db0c65..fd379e80e 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
@@ -138,6 +138,10 @@ export class VideoCommentComponent implements OnInit, OnChanges {
138 (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies 138 (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies
139 } 139 }
140 140
141 isChild () {
142 return this.parentComments.length !== 0
143 }
144
141 private getUserIfNeeded (account: Account) { 145 private getUserIfNeeded (account: Account) {
142 if (!account.userId) return 146 if (!account.userId) return
143 if (!this.authService.isLoggedIn()) return 147 if (!this.authService.isLoggedIn()) return
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
index e6778e1a9..a7e858069 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
@@ -11,7 +11,8 @@
11 cursor: pointer; 11 cursor: pointer;
12} 12}
13 13
14.glyphicon, .comment-thread-loading { 14.glyphicon,
15.comment-thread-loading {
15 margin-right: 5px; 16 margin-right: 5px;
16 display: inline-block; 17 display: inline-block;
17 font-size: 13px; 18 font-size: 13px;
@@ -40,7 +41,7 @@
40#dropdown-sort-comments { 41#dropdown-sort-comments {
41 font-weight: 600; 42 font-weight: 600;
42 text-transform: uppercase; 43 text-transform: uppercase;
43 border: none; 44 border: 0;
44 transform: translateY(-7%); 45 transform: translateY(-7%);
45} 46}
46 47
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
index e0e9f92e7..e1040fead 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
@@ -15,7 +15,9 @@
15 <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count"> 15 <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count">
16 <my-video-miniature 16 <my-video-miniature
17 [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow" 17 [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow"
18 (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"> 18 (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"
19 actorImageSize="32"
20 >
19 </my-video-miniature> 21 </my-video-miniature>
20 22
21 <hr *ngIf="!playlist && i == 0 && length > 1" /> 23 <hr *ngIf="!playlist && i == 0 && length > 1" />
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
index c9fae6f27..5e0373afc 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
@@ -8,7 +8,8 @@
8 margin-bottom: 25px; 8 margin-bottom: 25px;
9 flex-wrap: wrap-reverse; 9 flex-wrap: wrap-reverse;
10 10
11 .title-page.active, .title-page.title-page-single { 11 .title-page.active,
12 .title-page.title-page-single {
12 margin-bottom: unset; 13 margin-bottom: unset;
13 margin-right: .5rem !important; 14 margin-right: .5rem !important;
14 } 15 }
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.html b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
index a02373f2d..5f149cbd1 100644
--- a/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
@@ -1,21 +1,11 @@
1<div class="wrapper" [ngClass]="'avatar-' + size"> 1<div class="wrapper" [ngClass]="{ 'generic-channel': genericChannel }">
2 <ng-container *ngIf="!isChannelAvatarNull() && !genericChannel"> 2 <my-actor-avatar
3 <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> 3 class="channel" [channel]="video.channel"
4 <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" /> 4 [internalHref]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"
5 </a> 5 ></my-actor-avatar>
6 6
7 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar> 7 <my-actor-avatar
8</ng-container> 8 class="account" [account]="video.account"
9 9 [internalHref]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle">
10 <ng-container *ngIf="!isChannelAvatarNull() && genericChannel"> 10 </my-actor-avatar>
11 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
12
13 <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
14 <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" />
15 </a>
16 </ng-container>
17
18 <ng-container *ngIf="isChannelAvatarNull()">
19 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
20 </ng-container>
21</div> 11</div>
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
index 4998e85fa..20e32240c 100644
--- a/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
@@ -1,44 +1,42 @@
1@import '_mixins'; 1@import '_mixins';
2 2
3@mixin main {
4 @include actor-avatar-size(35px);
5}
6
7@mixin secondary {
8 height: 60%;
9 width: 60%;
10 position: absolute;
11 bottom: -5px;
12 right: -5px;
13 background-color: rgba(0, 0, 0, 0);
14}
15
3.wrapper { 16.wrapper {
4 $avatar-size: 35px; 17 @include actor-avatar-size(35px);
5 18
6 width: $avatar-size;
7 height: $avatar-size;
8 position: relative; 19 position: relative;
9 margin-right: 5px; 20 margin-right: 5px;
10 margin-bottom: 5px; 21 margin-bottom: 5px;
11 22
12 &.avatar-sm { 23 &.generic-channel {
13 width: 28px; 24 .account {
14 height: 28px; 25 @include main();
15 margin-bottom: 3px; 26 }
16 }
17 27
18 a { 28 .channel {
19 @include disable-outline; 29 display: none !important;
30 }
20 } 31 }
21 32
22 a img { 33 &:not(.generic-channel) {
23 height: 100%; 34 .account {
24 object-fit: cover; 35 @include secondary();
25 position: absolute;
26 top:50%;
27 left:50%;
28 transform: translate(-50%,-50%);
29 border-radius: 5px;
30
31 &:not(.channel-avatar) {
32 border-radius: 50%;
33 } 36 }
34 }
35 37
36 a:nth-of-type(2) img { 38 .channel {
37 height: 60%; 39 @include main();
38 width: 60%; 40 }
39 border: 2px solid pvar(--mainBackgroundColor);
40 transform: translateX(15%);
41 position: relative;
42 background-color: pvar(--mainBackgroundColor);
43 } 41 }
44} 42}
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
index 0b6e796df..63edd7bad 100644
--- a/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
@@ -10,7 +10,6 @@ export class VideoAvatarChannelComponent implements OnInit {
10 @Input() video: Video 10 @Input() video: Video
11 @Input() byAccount: string 11 @Input() byAccount: string
12 12
13 @Input() size: 'md' | 'sm' = 'md'
14 @Input() genericChannel: boolean 13 @Input() genericChannel: boolean
15 14
16 channelLinkTitle = '' 15 channelLinkTitle = ''
diff --git a/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss b/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss
index 0b0a2a899..b3f93b83c 100644
--- a/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss
+++ b/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss
@@ -45,7 +45,7 @@
45 45
46 my-global-icon { 46 my-global-icon {
47 &:not(.active) { 47 &:not(.active) {
48 opacity: .5 48 opacity: .5;
49 } 49 }
50 50
51 ::ng-deep { 51 ::ng-deep {
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.html b/client/src/app/+videos/+video-watch/video-watch.component.html
index eadb2148a..4779602d2 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.html
+++ b/client/src/app/+videos/+video-watch/video-watch.component.html
@@ -79,7 +79,7 @@
79 <span [innerHTML]="getRatePopoverText()"></span> 79 <span [innerHTML]="getRatePopoverText()"></span>
80 </ng-template> 80 </ng-template>
81 81
82 <div class="video-actions fullWidth justify-content-end"> 82 <div class="video-actions full-width justify-content-end">
83 <button 83 <button
84 [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()" 84 [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()"
85 class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike" 85 class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike"
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.scss b/client/src/app/+videos/+video-watch/video-watch.component.scss
index e8ad10a11..301762695 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/+videos/+video-watch/video-watch.component.scss
@@ -6,12 +6,12 @@
6$player-factor: 16/9; 6$player-factor: 16/9;
7$video-info-margin-left: 44px; 7$video-info-margin-left: 44px;
8 8
9@function getPlayerHeight($width){ 9@function getPlayerHeight ($width) {
10 @return calc(#{$width} / #{$player-factor}) 10 @return calc(#{$width} / #{$player-factor});
11} 11}
12 12
13@function getPlayerWidth($height){ 13@function getPlayerWidth ($height) {
14 @return calc(#{$height} * #{$player-factor}) 14 @return calc(#{$height} * #{$player-factor});
15} 15}
16 16
17@mixin playlist-below-player { 17@mixin playlist-below-player {
@@ -24,11 +24,11 @@ $video-info-margin-left: 44px;
24 24
25.root { 25.root {
26 &.theater-enabled #video-wrapper { 26 &.theater-enabled #video-wrapper {
27 $height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
28
27 flex-direction: column; 29 flex-direction: column;
28 justify-content: center; 30 justify-content: center;
29 31
30 $height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
31
32 #videojs-wrapper { 32 #videojs-wrapper {
33 width: 100%; 33 width: 100%;
34 height: $height; 34 height: $height;
@@ -141,7 +141,7 @@ $video-info-margin-left: 44px;
141 .video-info-first-row { 141 .video-info-first-row {
142 display: flex; 142 display: flex;
143 143
144 & > div:first-child { 144 > div:first-child {
145 flex-grow: 1; 145 flex-grow: 1;
146 } 146 }
147 147
@@ -207,7 +207,7 @@ $video-info-margin-left: 44px;
207 } 207 }
208 208
209 .video-actions-rates { 209 .video-actions-rates {
210 margin: 0 0 10px 0; 210 margin: 0 0 10px;
211 align-items: start; 211 align-items: start;
212 width: max-content; 212 width: max-content;
213 margin-left: auto; 213 margin-left: auto;
@@ -231,7 +231,7 @@ $video-info-margin-left: 44px;
231 font-size: 100%; 231 font-size: 100%;
232 font-weight: $font-semibold; 232 font-weight: $font-semibold;
233 display: inline-block; 233 display: inline-block;
234 padding: 0 10px 0 10px; 234 padding: 0 10px;
235 white-space: nowrap; 235 white-space: nowrap;
236 background-color: transparent !important; 236 background-color: transparent !important;
237 color: pvar(--actionButtonColor); 237 color: pvar(--actionButtonColor);
@@ -346,7 +346,8 @@ $video-info-margin-left: 44px;
346 } 346 }
347 } 347 }
348 348
349 .glyphicon, .description-loading { 349 .glyphicon,
350 .description-loading {
350 margin-left: 3px; 351 margin-left: 3px;
351 } 352 }
352 353
@@ -396,7 +397,7 @@ $video-info-margin-left: 44px;
396 &.video-attribute-tags { 397 &.video-attribute-tags {
397 .video-attribute-value:not(:nth-child(2)) { 398 .video-attribute-value:not(:nth-child(2)) {
398 &::before { 399 &::before {
399 content: ', ' 400 content: ', ';
400 } 401 }
401 } 402 }
402 } 403 }
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts
index cf6afd852..62ce7be2d 100644
--- a/client/src/app/+videos/+video-watch/video-watch.module.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.module.ts
@@ -20,7 +20,7 @@ import { TimestampRouteTransformerDirective } from './timestamp-route-transforme
20import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 20import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
21import { VideoWatchRoutingModule } from './video-watch-routing.module' 21import { VideoWatchRoutingModule } from './video-watch-routing.module'
22import { VideoWatchComponent } from './video-watch.component' 22import { VideoWatchComponent } from './video-watch.component'
23import { SharedAccountAvatarModule } from '../../shared/shared-account-avatar/shared-account-avatar.module' 23import { SharedActorImageModule } from '../../shared/shared-actor-image/shared-actor-image.module'
24import { VideoAvatarChannelComponent } from './video-avatar-channel.component' 24import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
25 25
26@NgModule({ 26@NgModule({
@@ -39,7 +39,7 @@ import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
39 SharedShareModal, 39 SharedShareModal,
40 SharedVideoModule, 40 SharedVideoModule,
41 SharedSupportModal, 41 SharedSupportModal,
42 SharedAccountAvatarModule 42 SharedActorImageModule
43 ], 43 ],
44 44
45 declarations: [ 45 declarations: [
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.html b/client/src/app/+videos/video-list/overview/video-overview.component.html
index 639a96c43..e21bffb6c 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.html
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.html
@@ -33,7 +33,7 @@
33 <div class="section channel videos" *ngFor="let object of overview.channels"> 33 <div class="section channel videos" *ngFor="let object of overview.channels">
34 <div class="section-title"> 34 <div class="section-title">
35 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]"> 35 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
36 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" /> 36 <my-actor-avatar [channel]="buildVideoChannel(object)"></my-actor-avatar>
37 37
38 <h2 class="section-title">{{ object.channel.displayName }}</h2> 38 <h2 class="section-title">{{ object.channel.displayName }}</h2>
39 </a> 39 </a>
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.scss b/client/src/app/+videos/video-list/overview/video-overview.component.scss
index ec73c628c..8fbac1b46 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.scss
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.scss
@@ -16,7 +16,7 @@
16 padding-top: 30px; 16 padding-top: 30px;
17 17
18 .section-title { 18 .section-title {
19 border-top: none !important; 19 border-top: 0 !important;
20 } 20 }
21 } 21 }
22 22
@@ -33,12 +33,14 @@
33 } 33 }
34 34
35 a { 35 a {
36 &:hover, &:focus:not(.focus-visible), &:active { 36 color: pvar(--mainForegroundColor);
37
38 &:hover,
39 &:focus:not(.focus-visible),
40 &:active {
37 text-decoration: none; 41 text-decoration: none;
38 outline: none; 42 outline: none;
39 } 43 }
40
41 color: pvar(--mainForegroundColor);
42 } 44 }
43 } 45 }
44 46
@@ -49,9 +51,10 @@
49 width: fit-content; 51 width: fit-content;
50 align-items: center; 52 align-items: center;
51 53
52 img { 54 my-actor-avatar {
53 @include channel-avatar(28px); 55 @include actor-avatar-size(28px);
54 56
57 font-size: initial;
55 margin-right: 8px; 58 margin-right: 8px;
56 } 59 }
57 } 60 }
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.ts b/client/src/app/+videos/video-list/overview/video-overview.component.ts
index b3be1d7b5..14532ca1e 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.ts
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.ts
@@ -45,8 +45,8 @@ export class VideoOverviewComponent implements OnInit {
45 return object.videos[0].byVideoChannel 45 return object.videos[0].byVideoChannel
46 } 46 }
47 47
48 buildVideoChannelAvatarUrl (object: { videos: Video[] }) { 48 buildVideoChannel (object: { videos: Video[] }) {
49 return object.videos[0].videoChannelAvatarUrl 49 return object.videos[0].channel
50 } 50 }
51 51
52 buildVideos (videos: Video[]) { 52 buildVideos (videos: Video[]) {
diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.scss b/client/src/app/+videos/video-list/trending/video-trending-header.component.scss
index 923a1d67a..6daacc78e 100644
--- a/client/src/app/+videos/video-list/trending/video-trending-header.component.scss
+++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.scss
@@ -14,4 +14,4 @@
14 height: 1rem; 14 height: 1rem;
15 margin-right: .1rem; 15 margin-right: .1rem;
16 } 16 }
17} \ No newline at end of file 17}
diff --git a/client/src/app/+videos/videos.module.ts b/client/src/app/+videos/videos.module.ts
index 61d012d63..8a35015d6 100644
--- a/client/src/app/+videos/videos.module.ts
+++ b/client/src/app/+videos/videos.module.ts
@@ -1,4 +1,5 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { SharedActorImageModule } from '@app/shared/shared-actor-image/shared-actor-image.module'
2import { SharedFormModule } from '@app/shared/shared-forms' 3import { SharedFormModule } from '@app/shared/shared-forms'
3import { SharedGlobalIconModule } from '@app/shared/shared-icons' 4import { SharedGlobalIconModule } from '@app/shared/shared-icons'
4import { SharedMainModule } from '@app/shared/shared-main' 5import { SharedMainModule } from '@app/shared/shared-main'
@@ -21,7 +22,8 @@ import { VideosComponent } from './videos.component'
21 SharedFormModule, 22 SharedFormModule,
22 SharedVideoMiniatureModule, 23 SharedVideoMiniatureModule,
23 SharedUserSubscriptionModule, 24 SharedUserSubscriptionModule,
24 SharedGlobalIconModule 25 SharedGlobalIconModule,
26 SharedActorImageModule
25 ], 27 ],
26 28
27 declarations: [ 29 declarations: [
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss
index e7d05369b..e21ada0f1 100644
--- a/client/src/app/app.component.scss
+++ b/client/src/app/app.component.scss
@@ -79,7 +79,7 @@
79 display: inline-block; 79 display: inline-block;
80 width: 23px; 80 width: 23px;
81 height: 24px; 81 height: 24px;
82 margin-right: .5rem; 82 margin-right: 0.5rem;
83 } 83 }
84 84
85 @media screen and (max-width: $mobile-view) { 85 @media screen and (max-width: $mobile-view) {
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 41c59cc86..3cec6d739 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -24,7 +24,7 @@ import { SharedGlobalIconModule } from './shared/shared-icons'
24import { SharedInstanceModule } from './shared/shared-instance' 24import { SharedInstanceModule } from './shared/shared-instance'
25import { SharedMainModule } from './shared/shared-main' 25import { SharedMainModule } from './shared/shared-main'
26import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings' 26import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings'
27import { SharedAccountAvatarModule } from './shared/shared-account-avatar/shared-account-avatar.module' 27import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module'
28 28
29registerLocaleData(localeOc, 'oc') 29registerLocaleData(localeOc, 'oc')
30 30
@@ -60,7 +60,7 @@ registerLocaleData(localeOc, 'oc')
60 SharedUserInterfaceSettingsModule, 60 SharedUserInterfaceSettingsModule,
61 SharedGlobalIconModule, 61 SharedGlobalIconModule,
62 SharedInstanceModule, 62 SharedInstanceModule,
63 SharedAccountAvatarModule, 63 SharedActorImageModule,
64 64
65 MetaModule.forRoot({ 65 MetaModule.forRoot({
66 provide: MetaLoader, 66 provide: MetaLoader,
diff --git a/client/src/app/core/hotkeys/hotkeys.component.scss b/client/src/app/core/hotkeys/hotkeys.component.scss
index a970260c9..b39ffa98d 100644
--- a/client/src/app/core/hotkeys/hotkeys.component.scss
+++ b/client/src/app/core/hotkeys/hotkeys.component.scss
@@ -12,16 +12,13 @@
12 left: 0; 12 left: 0;
13 color: #333; 13 color: #333;
14 font-size: 1em; 14 font-size: 1em;
15 background-color: rgba(255,255,255,0.9); 15 background-color: rgba(255, 255, 255, 0.9);
16} 16}
17 17
18.cfp-hotkeys-container.fade { 18.cfp-hotkeys-container.fade {
19 z-index: -1024; 19 z-index: -1024;
20 visibility: hidden; 20 visibility: hidden;
21 opacity: 0; 21 opacity: 0;
22 -webkit-transition: opacity 0.15s linear;
23 -moz-transition: opacity 0.15s linear;
24 -o-transition: opacity 0.15s linear;
25 transition: opacity 0.15s linear; 22 transition: opacity 0.15s linear;
26} 23}
27 24
diff --git a/client/src/app/core/rest/rest-table.ts b/client/src/app/core/rest/rest-table.ts
index 32c1db446..a5b48f10c 100644
--- a/client/src/app/core/rest/rest-table.ts
+++ b/client/src/app/core/rest/rest-table.ts
@@ -1,8 +1,6 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import { LazyLoadEvent, SortMeta } from 'primeng/api' 2import { LazyLoadEvent, SortMeta } from 'primeng/api'
3import { Subject } from 'rxjs' 3import { ActivatedRoute, Router } from '@angular/router'
4import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
5import { ActivatedRoute, Params, Router } from '@angular/router'
6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
7import { RestPagination } from './rest-pagination' 5import { RestPagination } from './rest-pagination'
8 6
@@ -14,14 +12,11 @@ export abstract class RestTable {
14 abstract sort: SortMeta 12 abstract sort: SortMeta
15 abstract pagination: RestPagination 13 abstract pagination: RestPagination
16 14
17 search: string
18 rowsPerPageOptions = [ 10, 20, 50, 100 ] 15 rowsPerPageOptions = [ 10, 20, 50, 100 ]
19 rowsPerPage = this.rowsPerPageOptions[0] 16 rowsPerPage = this.rowsPerPageOptions[0]
20 expandedRows = {} 17 expandedRows = {}
21 18
22 baseRoute: string 19 search: string
23
24 protected searchStream: Subject<string>
25 20
26 protected route: ActivatedRoute 21 protected route: ActivatedRoute
27 protected router: Router 22 protected router: Router
@@ -30,7 +25,6 @@ export abstract class RestTable {
30 25
31 initialize () { 26 initialize () {
32 this.loadSort() 27 this.loadSort()
33 this.initSearch()
34 } 28 }
35 29
36 loadSort () { 30 loadSort () {
@@ -58,7 +52,7 @@ export abstract class RestTable {
58 count: this.rowsPerPage 52 count: this.rowsPerPage
59 } 53 }
60 54
61 this.loadData() 55 this.reloadData()
62 this.saveSort() 56 this.saveSort()
63 } 57 }
64 58
@@ -66,55 +60,6 @@ export abstract class RestTable {
66 peertubeLocalStorage.setItem(this.getSortLocalStorageKey(), JSON.stringify(this.sort)) 60 peertubeLocalStorage.setItem(this.getSortLocalStorageKey(), JSON.stringify(this.sort))
67 } 61 }
68 62
69 initSearch () {
70 this.searchStream = new Subject()
71
72 this.searchStream
73 .pipe(
74 debounceTime(400),
75 distinctUntilChanged()
76 )
77 .subscribe(search => {
78 this.search = search
79
80 logger('On search %s.', this.search)
81
82 this.loadData()
83 })
84 }
85
86 onSearch (event: Event) {
87 const target = event.target as HTMLInputElement
88 this.searchStream.next(target.value)
89
90 this.setQueryParams((event.target as HTMLInputElement).value)
91 }
92
93 setQueryParams (search: string) {
94 if (!this.baseRoute) return
95
96 const queryParams: Params = {}
97
98 if (search) Object.assign(queryParams, { search })
99 this.router.navigate([ this.baseRoute ], { queryParams })
100 }
101
102 resetTableFilter () {
103 this.setTableFilter('')
104 this.setQueryParams('')
105 this.resetSearch()
106 }
107
108 listenToSearchChange () {
109 this.route.queryParams
110 .subscribe(params => {
111 this.search = params.search || ''
112
113 // Primeng table will run an event to load data
114 this.setTableFilter(this.search)
115 })
116 }
117
118 onPage (event: { first: number, rows: number }) { 63 onPage (event: { first: number, rows: number }) {
119 logger('On page %o.', event) 64 logger('On page %o.', event)
120 65
@@ -125,28 +70,18 @@ export abstract class RestTable {
125 count: this.rowsPerPage 70 count: this.rowsPerPage
126 } 71 }
127 72
128 this.loadData() 73 this.reloadData()
129 } 74 }
130 75
131 this.expandedRows = {} 76 this.expandedRows = {}
132 } 77 }
133 78
134 setTableFilter (filter: string, triggerEvent = true) { 79 onSearch (search: string) {
135 // FIXME: cannot use ViewChild, so create a component for the filter input 80 this.search = search
136 const filterInput = document.getElementById('table-filter') as HTMLInputElement 81 this.reloadData()
137 if (!filterInput) return
138
139 filterInput.value = filter
140
141 if (triggerEvent) filterInput.dispatchEvent(new Event('keyup'))
142 }
143
144 resetSearch () {
145 this.searchStream.next('')
146 this.setTableFilter('')
147 } 82 }
148 83
149 protected abstract loadData (): void 84 protected abstract reloadData (): void
150 85
151 private getSortLocalStorageKey () { 86 private getSortLocalStorageKey () {
152 return 'rest-table-sort-' + this.getIdentifier() 87 return 'rest-table-sort-' + this.getIdentifier()
diff --git a/client/src/app/core/rest/rest.service.ts b/client/src/app/core/rest/rest.service.ts
index 4f1fc8848..1696e6709 100644
--- a/client/src/app/core/rest/rest.service.ts
+++ b/client/src/app/core/rest/rest.service.ts
@@ -90,14 +90,20 @@ export class RestService {
90 90
91 const matchedTokens = tokens.filter(t => t.startsWith(prefix)) 91 const matchedTokens = tokens.filter(t => t.startsWith(prefix))
92 .map(t => t.slice(prefix.length)) // Keep the value filter 92 .map(t => t.slice(prefix.length)) // Keep the value filter
93 .map(t => t.replace(/^"|"$/g, '')) 93 .map(t => t.replace(/^"|"$/g, '')) // Remove ""
94 .map(t => { 94 .map(t => {
95 if (prefixObj.handler) return prefixObj.handler(t) 95 if (prefixObj.handler) return prefixObj.handler(t)
96 96
97 if (prefixObj.isBoolean) {
98 if (t === 'true') return true
99 if (t === 'false') return false
100
101 return undefined
102 }
103
97 return t 104 return t
98 }) 105 })
99 .filter(t => !!t || t === 0) 106 .filter(t => t !== null && t !== undefined)
100 .map(t => prefixObj.isBoolean ? t === 'true' : t)
101 107
102 if (matchedTokens.length === 0) continue 108 if (matchedTokens.length === 0) continue
103 109
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts
index 3de83152c..47db985e1 100644
--- a/client/src/app/core/users/user.service.ts
+++ b/client/src/app/core/users/user.service.ts
@@ -320,13 +320,7 @@ export class UserService {
320 const filters = this.restService.parseQueryStringFilter(search, { 320 const filters = this.restService.parseQueryStringFilter(search, {
321 blocked: { 321 blocked: {
322 prefix: 'banned:', 322 prefix: 'banned:',
323 isBoolean: true, 323 isBoolean: true
324 handler: v => {
325 if (v === 'true') return v
326 if (v === 'false') return v
327
328 return undefined
329 }
330 } 324 }
331 }) 325 })
332 326
diff --git a/client/src/app/core/wrappers/screen.service.ts b/client/src/app/core/wrappers/screen.service.ts
index c133b5fe9..fd8268b35 100644
--- a/client/src/app/core/wrappers/screen.service.ts
+++ b/client/src/app/core/wrappers/screen.service.ts
@@ -39,9 +39,9 @@ export class ScreenService {
39 let numberOfVideos = 1 39 let numberOfVideos = 1
40 40
41 if (screenWidth > 1850) numberOfVideos = 5 41 if (screenWidth > 1850) numberOfVideos = 5
42 else if (screenWidth > 1600) numberOfVideos = 4 42 else if (screenWidth > 1410) numberOfVideos = 4
43 else if (screenWidth > 1370) numberOfVideos = 3 43 else if (screenWidth > 1120) numberOfVideos = 3
44 else if (screenWidth > 1100) numberOfVideos = 2 44 else if (screenWidth > 890) numberOfVideos = 2
45 45
46 return numberOfVideos 46 return numberOfVideos
47 } 47 }
diff --git a/client/src/app/header/search-typeahead.component.scss b/client/src/app/header/search-typeahead.component.scss
index c754a99d1..3e0350ba0 100644
--- a/client/src/app/header/search-typeahead.component.scss
+++ b/client/src/app/header/search-typeahead.component.scss
@@ -44,7 +44,8 @@ li.suggestion {
44 44
45 // soft border-radius for the last suggestion and the link inside 45 // soft border-radius for the last suggestion and the link inside
46 &:last-of-type { 46 &:last-of-type {
47 &, & ::ng-deep a { 47 &,
48 ::ng-deep a {
48 border-bottom-right-radius: 3px; 49 border-bottom-right-radius: 3px;
49 border-bottom-left-radius: 3px; 50 border-bottom-left-radius: 3px;
50 } 51 }
@@ -74,7 +75,7 @@ li.suggestion {
74#typeahead-container { 75#typeahead-container {
75 input { 76 input {
76 border: 1px solid pvar(--mainBackgroundColor) !important; 77 border: 1px solid pvar(--mainBackgroundColor) !important;
77 box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 20px 0px; 78 box-shadow: rgba(0, 0, 0, 0.1) 0 1px 20px 0;
78 flex-grow: 1; 79 flex-grow: 1;
79 transition: box-shadow .3s ease, width .2s ease; 80 transition: box-shadow .3s ease, width .2s ease;
80 } 81 }
@@ -95,7 +96,7 @@ li.suggestion {
95 right: 10px; 96 right: 10px;
96 } 97 }
97 98
98 & > div:last-child { 99 > div:last-child {
99 // we have to switch the display and not the opacity, 100 // we have to switch the display and not the opacity,
100 // to avoid clashing with the rest of the interface. 101 // to avoid clashing with the rest of the interface.
101 display: none; 102 display: none;
@@ -103,7 +104,7 @@ li.suggestion {
103 104
104 &:focus, 105 &:focus,
105 ::ng-deep &:focus-within { 106 ::ng-deep &:focus-within {
106 & > div:last-child { 107 > div:last-child {
107 @media screen and (min-width: $mobile-view) { 108 @media screen and (min-width: $mobile-view) {
108 display: initial !important; 109 display: initial !important;
109 } 110 }
@@ -111,12 +112,12 @@ li.suggestion {
111 #typeahead-help, 112 #typeahead-help,
112 #typeahead-instructions, 113 #typeahead-instructions,
113 li.suggestion { 114 li.suggestion {
114 box-shadow: rgba(0, 0, 0, 0.2) 0px 10px 20px -5px; 115 box-shadow: rgba(0, 0, 0, 0.2) 0 10px 20px -5px;
115 } 116 }
116 } 117 }
117 118
118 ::ng-deep input { 119 ::ng-deep input {
119 box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 20px 0px; 120 box-shadow: rgba(0, 0, 0, 0.2) 0 1px 20px 0;
120 border-end-start-radius: 0; 121 border-end-start-radius: 0;
121 border-end-end-radius: 0; 122 border-end-end-radius: 0;
122 123
diff --git a/client/src/app/header/suggestion.component.scss b/client/src/app/header/suggestion.component.scss
index 692a81daa..9163de0b1 100644
--- a/client/src/app/header/suggestion.component.scss
+++ b/client/src/app/header/suggestion.component.scss
@@ -2,9 +2,11 @@
2 2
3a { 3a {
4 @include disable-default-a-behaviour; 4 @include disable-default-a-behaviour;
5
5 width: 100%; 6 width: 100%;
6 7
7 &, &:hover { 8 &,
9 &:hover {
8 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
9 11
10 &.focus-visible { 12 &.focus-visible {
@@ -23,10 +25,10 @@ a {
23} 25}
24 26
25my-global-icon { 27my-global-icon {
28 @include apply-svg-color(pvar(--mainForegroundColor));
29
26 width: 17px; 30 width: 17px;
27 position: relative; 31 position: relative;
28 top: -2px; 32 top: -2px;
29 margin: 5px; 33 margin: 5px;
30
31 @include apply-svg-color(pvar(--mainForegroundColor));
32} 34}
diff --git a/client/src/app/menu/language-chooser.component.scss b/client/src/app/menu/language-chooser.component.scss
index 6226a85cb..800b1ebef 100644
--- a/client/src/app/menu/language-chooser.component.scss
+++ b/client/src/app/menu/language-chooser.component.scss
@@ -5,12 +5,12 @@
5 @include peertube-button-link; 5 @include peertube-button-link;
6 @include orange-button; 6 @include orange-button;
7 7
8 border-radius: 0;
9
8 &.focus-visible, 10 &.focus-visible,
9 &:focus { 11 &:focus {
10 box-shadow: none; 12 box-shadow: none;
11 } 13 }
12
13 border-radius: 0;
14} 14}
15 15
16.modal-body { 16.modal-body {
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html
index df5c7971d..2e07deca2 100644
--- a/client/src/app/menu/menu.component.html
+++ b/client/src/app/menu/menu.component.html
@@ -5,7 +5,7 @@
5 <div> 5 <div>
6 <div class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left" [container]="dropdownContainer" (openChange)="onDropdownOpenChange($event)" autoClose="outside"> 6 <div class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left" [container]="dropdownContainer" (openChange)="onDropdownOpenChange($event)" autoClose="outside">
7 <div ngbDropdownToggle> 7 <div ngbDropdownToggle>
8 <my-account-avatar [account]="user.account" size="34"></my-account-avatar> 8 <my-actor-avatar [account]="user.account" size="34"></my-actor-avatar>
9 <div class="logged-in-info"> 9 <div class="logged-in-info">
10 <div class="logged-in-display-name">{{ user.account?.displayName }}</div> 10 <div class="logged-in-display-name">{{ user.account?.displayName }}</div>
11 11
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss
index 00d1a1f69..d0edd820e 100644
--- a/client/src/app/menu/menu.component.scss
+++ b/client/src/app/menu/menu.component.scss
@@ -24,8 +24,9 @@ $footer-links-base-opacity: .8;
24 background-color: rgba(255, 255, 255, 0.15); 24 background-color: rgba(255, 255, 255, 0.15);
25 } 25 }
26 26
27 &:hover, &.focus-visible { 27 &:hover,
28 background-color: rgba(255, 255, 255, 0.10); 28 &.focus-visible {
29 background-color: rgba(255, 255, 255, 0.1);
29 } 30 }
30 31
31 my-global-icon { 32 my-global-icon {
@@ -60,7 +61,8 @@ menu {
60 margin: 0; 61 margin: 0;
61 padding: 0; 62 padding: 0;
62 63
63 &:focus, &:hover { 64 &:focus,
65 &:hover {
64 overflow-y: auto; 66 overflow-y: auto;
65 } 67 }
66 68
@@ -125,7 +127,7 @@ my-notification {
125 line-height: 1; 127 line-height: 1;
126 128
127 &.show { 129 &.show {
128 background-color: rgba(255, 255, 255, 0.20); 130 background-color: rgba(255, 255, 255, 0.2);
129 box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325); 131 box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325);
130 } 132 }
131 133
@@ -158,14 +160,14 @@ my-notification {
158 position: absolute; 160 position: absolute;
159 right: -35px; 161 right: -35px;
160 top: -8px; 162 top: -8px;
161 color: grey; 163 color: #808080;
162 width: $main-radius; 164 width: $main-radius;
163 } 165 }
164 } 166 }
165 167
166 .dropdown-toggle { 168 .dropdown-toggle {
167 &::after { 169 &::after {
168 border: none; 170 border: 0;
169 } 171 }
170 } 172 }
171 173
@@ -177,7 +179,7 @@ my-notification {
177 } 179 }
178} 180}
179 181
180my-account-avatar { 182my-actor-avatar {
181 margin-right: 10px; 183 margin-right: 10px;
182} 184}
183 185
@@ -193,11 +195,11 @@ my-account-avatar {
193} 195}
194 196
195.logged-in-display-name { 197.logged-in-display-name {
198 @include disable-default-a-behaviour;
199
196 font-size: 16px; 200 font-size: 16px;
197 font-weight: $font-semibold; 201 font-weight: $font-semibold;
198 color: pvar(--menuForegroundColor); 202 color: pvar(--menuForegroundColor);
199
200 @include disable-default-a-behaviour;
201} 203}
202 204
203.logged-in-username { 205.logged-in-username {
@@ -251,7 +253,7 @@ my-account-avatar {
251} 253}
252 254
253.login-buttons-block { 255.login-buttons-block {
254 margin: 30px 25px 35px 25px; 256 margin: 30px 25px 35px;
255 257
256 > a { 258 > a {
257 display: block; 259 display: block;
@@ -305,7 +307,8 @@ my-account-avatar {
305} 307}
306 308
307.footer-links { 309.footer-links {
308 &, > div { 310 &,
311 > div {
309 display: flex; 312 display: flex;
310 flex-wrap: wrap; 313 flex-wrap: wrap;
311 } 314 }
@@ -388,29 +391,29 @@ my-account-avatar {
388 .dropdown-item:hover, 391 .dropdown-item:hover,
389 .dropdown-item:active { 392 .dropdown-item:active {
390 &.settings-sensitive my-global-icon ::ng-deep svg { 393 &.settings-sensitive my-global-icon ::ng-deep svg {
391 margin-top: 0px !important; 394 margin-top: 0 !important;
392 } 395 }
393 } 396 }
394} 397}
395 398
396my-global-icon { 399my-global-icon {
397 &[iconName="playlists"] { 400 &[iconName=playlists] {
398 height: 24px; 401 height: 24px;
399 width: 24px; 402 width: 24px;
400 403
401 margin-right: 16px; 404 margin-right: 16px;
402 } 405 }
403 406
404 &[iconName="videos"] { 407 &[iconName=videos] {
405 position: relative; 408 position: relative;
406 right: -1px; 409 right: -1px;
407 } 410 }
408 411
409 &[iconName="channel"] { 412 &[iconName=channel] {
410 margin-top: -2px; 413 margin-top: -2px;
411 } 414 }
412 415
413 &[iconName="sign-out"] { 416 &[iconName='sign-out'] {
414 position: relative; 417 position: relative;
415 right: -2px; 418 right: -2px;
416 height: 20px; 419 height: 20px;
diff --git a/client/src/app/menu/notification.component.scss b/client/src/app/menu/notification.component.scss
index c65787779..554c20ca9 100644
--- a/client/src/app/menu/notification.component.scss
+++ b/client/src/app/menu/notification.component.scss
@@ -16,19 +16,20 @@
16.notification-inbox-popover, 16.notification-inbox-popover,
17.notification-inbox-link a { 17.notification-inbox-link a {
18 @include apply-svg-color(#808080); 18 @include apply-svg-color(#808080);
19 ::ng-deep {
20 svg {
21 transition: color .1s ease-in-out;
22 }
23 }
24 19
25 transition: all .1s ease-in-out; 20 transition: all .1s ease-in-out;
26 border-radius: 25px; 21 border-radius: 25px;
27 cursor: pointer; 22 cursor: pointer;
28 23
29 &:hover, &:active { 24 ::ng-deep svg {
30 background-color: rgba(255, 255, 255, 0.15); 25 transition: color .1s ease-in-out;
26 }
27
28 &:hover,
29 &:active {
31 @include apply-svg-color(#fff); 30 @include apply-svg-color(#fff);
31
32 background-color: rgba(255, 255, 255, 0.15);
32 } 33 }
33} 34}
34 35
@@ -59,7 +60,7 @@
59 font-size: 14px; 60 font-size: 14px;
60 font-family: $main-fonts; 61 font-family: $main-fonts;
61 width: 400px; 62 width: 400px;
62 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.30); 63 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
63 64
64 .loader { 65 .loader {
65 display: flex; 66 display: flex;
@@ -80,7 +81,7 @@
80 max-height: 500px; 81 max-height: 500px;
81 } 82 }
82 83
83 & > my-user-notifications:nth-child(2) { 84 > my-user-notifications:nth-child(2) {
84 overflow-y: auto; 85 overflow-y: auto;
85 flex-grow: 1; 86 flex-grow: 1;
86 } 87 }
@@ -110,7 +111,8 @@
110 background: transparent; 111 background: transparent;
111 } 112 }
112 113
113 a, button { 114 a,
115 button {
114 color: rgba(20, 20, 20, 0.5); 116 color: rgba(20, 20, 20, 0.5);
115 117
116 &:hover:not(:disabled) { 118 &:hover:not(:disabled) {
@@ -133,7 +135,8 @@
133 } 135 }
134} 136}
135 137
136.notification-inbox-popover, .notification-inbox-link { 138.notification-inbox-popover,
139.notification-inbox-link {
137 cursor: pointer; 140 cursor: pointer;
138 position: relative; 141 position: relative;
139 142
diff --git a/client/src/app/modal/welcome-modal.component.scss b/client/src/app/modal/welcome-modal.component.scss
index 28d5dc49c..5e9e3dc51 100644
--- a/client/src/app/modal/welcome-modal.component.scss
+++ b/client/src/app/modal/welcome-modal.component.scss
@@ -42,7 +42,7 @@ li {
42 text-align: center; 42 text-align: center;
43 font-weight: 600; 43 font-weight: 600;
44 font-size: 18px; 44 font-size: 18px;
45 margin: 20px 0 40px 0; 45 margin: 20px 0 40px;
46} 46}
47 47
48.columns { 48.columns {
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.html b/client/src/app/shared/shared-abuse-list/abuse-details.component.html
index 658d42537..ca68de4b1 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.html
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.html
@@ -7,16 +7,16 @@
7 <span class="col-3 moderation-expanded-label" i18n>Reporter</span> 7 <span class="col-3 moderation-expanded-label" i18n>Reporter</span>
8 8
9 <span class="col-9 moderation-expanded-text"> 9 <span class="col-9 moderation-expanded-text">
10 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" 10 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }"
11 class="chip" 11 class="chip"
12 > 12 >
13 <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar> 13 <my-actor-avatar size="18" [account]="abuse.reporterAccount"></my-actor-avatar>
14 <div> 14 <div>
15 <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span> 15 <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span>
16 </div> 16 </div>
17 </a> 17 </a>
18 18
19 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" 19 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }"
20 class="ml-auto text-muted abuse-details-links" i18n 20 class="ml-auto text-muted abuse-details-links" i18n
21 > 21 >
22 {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span> 22 {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
@@ -27,16 +27,16 @@
27 <div class="d-flex" *ngIf="abuse.flaggedAccount"> 27 <div class="d-flex" *ngIf="abuse.flaggedAccount">
28 <span class="col-3 moderation-expanded-label" i18n>Reportee</span> 28 <span class="col-3 moderation-expanded-label" i18n>Reportee</span>
29 <span class="col-9 moderation-expanded-text"> 29 <span class="col-9 moderation-expanded-text">
30 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }" 30 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }"
31 class="chip" 31 class="chip"
32 > 32 >
33 <my-account-avatar [account]="abuse.flaggedAccount"></my-account-avatar> 33 <my-actor-avatar size="18" [account]="abuse.flaggedAccount"></my-actor-avatar>
34 <div> 34 <div>
35 <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span> 35 <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span>
36 </div> 36 </div>
37 </a> 37 </a>
38 38
39 <a *ngIf="isAdminView" [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }" 39 <a *ngIf="isAdminView" [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }"
40 class="ml-auto text-muted abuse-details-links" i18n 40 class="ml-auto text-muted abuse-details-links" i18n
41 > 41 >
42 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span> 42 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
@@ -53,7 +53,7 @@
53 <div class="mt-3 d-flex"> 53 <div class="mt-3 d-flex">
54 <span class="col-3 moderation-expanded-label"> 54 <span class="col-3 moderation-expanded-label">
55 <ng-container i18n>Report</ng-container> 55 <ng-container i18n>Report</ng-container>
56 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a> 56 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a>
57 </span> 57 </span>
58 <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span> 58 <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span>
59 </div> 59 </div>
@@ -61,7 +61,7 @@
61 <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex"> 61 <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex">
62 <span class="col-3"></span> 62 <span class="col-3"></span>
63 <span class="col-9"> 63 <span class="col-9">
64 <a *ngFor="let reason of getPredefinedReasons()" [routerLink]="[ baseRoute ]" 64 <a *ngFor="let reason of getPredefinedReasons()" [routerLink]="[ '.' ]"
65 [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" 65 [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light"
66 > 66 >
67 <div>{{ reason.label }}</div> 67 <div>{{ reason.label }}</div>
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
index e8ce7e678..14674c5f0 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
@@ -1,6 +1,5 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { durationToString } from '@app/helpers' 2import { durationToString } from '@app/helpers'
3import { Account } from '@app/shared/shared-main'
4import { AbusePredefinedReasonsString } from '@shared/models' 3import { AbusePredefinedReasonsString } from '@shared/models'
5import { ProcessedAbuse } from './processed-abuse.model' 4import { ProcessedAbuse } from './processed-abuse.model'
6 5
@@ -12,7 +11,6 @@ import { ProcessedAbuse } from './processed-abuse.model'
12export class AbuseDetailsComponent { 11export class AbuseDetailsComponent {
13 @Input() abuse: ProcessedAbuse 12 @Input() abuse: ProcessedAbuse
14 @Input() isAdminView: boolean 13 @Input() isAdminView: boolean
15 @Input() baseRoute: string
16 14
17 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string } 15 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string }
18 16
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
index 29b51f09c..b1c065c7a 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
@@ -1,6 +1,7 @@
1<p-table 1<p-table
2 [value]="abuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="abuses" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" 3 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
4 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" 6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
6 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 7 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -8,28 +9,7 @@
8 <ng-template pTemplate="caption"> 9 <ng-template pTemplate="caption">
9 <div class="caption"> 10 <div class="caption">
10 <div class="ml-auto"> 11 <div class="ml-auto">
11 <div class="input-group has-feedback has-clear"> 12 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
12 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
13 <div class="input-group-text" ngbDropdownToggle>
14 <span class="caret" aria-haspopup="menu" role="button"></span>
15 </div>
16
17 <div role="menu" ngbDropdownMenu>
18 <h6 class="dropdown-header" i18n>Advanced report filters</h6>
19 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a>
20 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a>
21 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a>
22 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a>
23 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a>
24 </div>
25 </div>
26 <input
27 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
28 (keyup)="onSearch($event)"
29 >
30 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
31 <span class="sr-only" i18n>Clear filters</span>
32 </div>
33 </div> 13 </div>
34 </div> 14 </div>
35 </ng-template> 15 </ng-template>
@@ -65,7 +45,7 @@
65 <td *ngIf="isAdminView()"> 45 <td *ngIf="isAdminView()">
66 <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 46 <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
67 <div class="chip two-lines"> 47 <div class="chip two-lines">
68 <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar> 48 <my-actor-avatar [account]="abuse.reporterAccount"></my-actor-avatar>
69 <div> 49 <div>
70 {{ abuse.reporterAccount.displayName }} 50 {{ abuse.reporterAccount.displayName }}
71 <span>{{ abuse.reporterAccount.nameWithHost }}</span> 51 <span>{{ abuse.reporterAccount.nameWithHost }}</span>
@@ -171,7 +151,7 @@
171 <ng-template pTemplate="rowexpansion" let-abuse> 151 <ng-template pTemplate="rowexpansion" let-abuse>
172 <tr> 152 <tr>
173 <td class="expand-cell" colspan="8"> 153 <td class="expand-cell" colspan="8">
174 <my-abuse-details [abuse]="abuse" [baseRoute]="baseRoute" [isAdminView]="isAdminView()"></my-abuse-details> 154 <my-abuse-details [abuse]="abuse" [isAdminView]="isAdminView()"></my-abuse-details>
175 </td> 155 </td>
176 </tr> 156 </tr>
177 </ng-template> 157 </ng-template>
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index 8b5771237..4dc2b4f10 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -3,7 +3,7 @@ import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 3import { SortMeta } from 'primeng/api'
4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
5import { environment } from 'src/environments/environment' 5import { environment } from 'src/environments/environment'
6import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core' 6import { Component, Input, OnInit, ViewChild } from '@angular/core'
7import { DomSanitizer } from '@angular/platform-browser' 7import { DomSanitizer } from '@angular/platform-browser'
8import { ActivatedRoute, Router } from '@angular/router' 8import { ActivatedRoute, Router } from '@angular/router'
9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' 9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
@@ -11,6 +11,7 @@ import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared
11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' 11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
12import { VideoCommentService } from '@app/shared/shared-video-comment' 12import { VideoCommentService } from '@app/shared/shared-video-comment'
13import { AbuseState, AdminAbuse } from '@shared/models' 13import { AbuseState, AdminAbuse } from '@shared/models'
14import { AdvancedInputFilter } from '../shared-forms'
14import { AbuseMessageModalComponent } from './abuse-message-modal.component' 15import { AbuseMessageModalComponent } from './abuse-message-modal.component'
15import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 16import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
16import { ProcessedAbuse } from './processed-abuse.model' 17import { ProcessedAbuse } from './processed-abuse.model'
@@ -22,9 +23,8 @@ const logger = debug('peertube:moderation:AbuseListTableComponent')
22 templateUrl: './abuse-list-table.component.html', 23 templateUrl: './abuse-list-table.component.html',
23 styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ] 24 styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ]
24}) 25})
25export class AbuseListTableComponent extends RestTable implements OnInit, AfterViewInit { 26export class AbuseListTableComponent extends RestTable implements OnInit {
26 @Input() viewType: 'admin' | 'user' 27 @Input() viewType: 'admin' | 'user'
27 @Input() baseRoute: string
28 28
29 @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent 29 @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent
30 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent 30 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
@@ -36,6 +36,29 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
36 36
37 abuseActions: DropdownAction<ProcessedAbuse>[][] = [] 37 abuseActions: DropdownAction<ProcessedAbuse>[][] = []
38 38
39 inputFilters: AdvancedInputFilter[] = [
40 {
41 queryParams: { 'search': 'state:pending' },
42 label: $localize`Unsolved reports`
43 },
44 {
45 queryParams: { 'search': 'state:accepted' },
46 label: $localize`Accepted reports`
47 },
48 {
49 queryParams: { 'search': 'state:rejected' },
50 label: $localize`Refused reports`
51 },
52 {
53 queryParams: { 'search': 'videoIs:blacklisted' },
54 label: $localize`Reports with blocked videos`
55 },
56 {
57 queryParams: { 'search': 'videoIs:deleted' },
58 label: $localize`Reports with deleted videos`
59 }
60 ]
61
39 constructor ( 62 constructor (
40 protected route: ActivatedRoute, 63 protected route: ActivatedRoute,
41 protected router: Router, 64 protected router: Router,
@@ -66,11 +89,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
66 ] 89 ]
67 90
68 this.initialize() 91 this.initialize()
69 this.listenToSearchChange()
70 }
71
72 ngAfterViewInit () {
73 if (this.search) this.setTableFilter(this.search, false)
74 } 92 }
75 93
76 isAdminView () { 94 isAdminView () {
@@ -86,7 +104,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
86 } 104 }
87 105
88 onModerationCommentUpdated () { 106 onModerationCommentUpdated () {
89 this.loadData() 107 this.reloadData()
90 } 108 }
91 109
92 isAbuseAccepted (abuse: AdminAbuse) { 110 isAbuseAccepted (abuse: AdminAbuse) {
@@ -129,7 +147,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
129 this.abuseService.removeAbuse(abuse).subscribe( 147 this.abuseService.removeAbuse(abuse).subscribe(
130 () => { 148 () => {
131 this.notifier.success($localize`Abuse deleted.`) 149 this.notifier.success($localize`Abuse deleted.`)
132 this.loadData() 150 this.reloadData()
133 }, 151 },
134 152
135 err => this.notifier.error(err.message) 153 err => this.notifier.error(err.message)
@@ -139,7 +157,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
139 updateAbuseState (abuse: AdminAbuse, state: AbuseState) { 157 updateAbuseState (abuse: AdminAbuse, state: AbuseState) {
140 this.abuseService.updateAbuse(abuse, { state }) 158 this.abuseService.updateAbuse(abuse, { state })
141 .subscribe( 159 .subscribe(
142 () => this.loadData(), 160 () => this.reloadData(),
143 161
144 err => this.notifier.error(err.message) 162 err => this.notifier.error(err.message)
145 ) 163 )
@@ -166,7 +184,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
166 return Actor.IS_LOCAL(abuse.reporterAccount.host) 184 return Actor.IS_LOCAL(abuse.reporterAccount.host)
167 } 185 }
168 186
169 protected loadData () { 187 protected reloadData () {
170 logger('Loading data.') 188 logger('Loading data.')
171 189
172 const options = { 190 const options = {
diff --git a/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts b/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
index 19b6d456d..8f3830a17 100644
--- a/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
+++ b/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
@@ -10,7 +10,7 @@ import { AbuseDetailsComponent } from './abuse-details.component'
10import { AbuseListTableComponent } from './abuse-list-table.component' 10import { AbuseListTableComponent } from './abuse-list-table.component'
11import { AbuseMessageModalComponent } from './abuse-message-modal.component' 11import { AbuseMessageModalComponent } from './abuse-message-modal.component'
12import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 12import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
13import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' 13import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
14 14
15@NgModule({ 15@NgModule({
16 imports: [ 16 imports: [
@@ -21,7 +21,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou
21 SharedModerationModule, 21 SharedModerationModule,
22 SharedGlobalIconModule, 22 SharedGlobalIconModule,
23 SharedVideoCommentModule, 23 SharedVideoCommentModule,
24 SharedAccountAvatarModule 24 SharedActorImageModule
25 ], 25 ],
26 26
27 declarations: [ 27 declarations: [
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.html b/client/src/app/shared/shared-account-avatar/account-avatar.component.html
deleted file mode 100644
index ca4ceb12f..000000000
--- a/client/src/app/shared/shared-account-avatar/account-avatar.component.html
+++ /dev/null
@@ -1,15 +0,0 @@
1<ng-template #img>
2 <img [class]="class" [src]="avatarUrl" i18n-alt alt="Account avatar" />
3</ng-template>
4
5<a *ngIf="account && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
6 <ng-template *ngTemplateOutlet="img"></ng-template>
7</a>
8
9<a *ngIf="account && internalHref" [routerLink]="internalHref" [title]="title">
10 <ng-template *ngTemplateOutlet="img"></ng-template>
11</a>
12
13<ng-container *ngIf="!account || (!href && !internalHref)">
14 <ng-template *ngTemplateOutlet="img"></ng-template>
15</ng-container>
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss b/client/src/app/shared/shared-account-avatar/account-avatar.component.scss
deleted file mode 100644
index bb941d712..000000000
--- a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss
+++ /dev/null
@@ -1,22 +0,0 @@
1@import '_variables';
2@import '_mixins';
3
4.avatar-25 {
5 @include avatar(25px);
6}
7
8.avatar-34 {
9 @include avatar(34px);
10}
11
12.avatar-36 {
13 @include avatar(36px);
14}
15
16.avatar-40 {
17 @include avatar(40px);
18}
19
20.avatar-120 {
21 @include avatar(120px);
22} \ No newline at end of file
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts b/client/src/app/shared/shared-account-avatar/account-avatar.component.ts
deleted file mode 100644
index 02a0a18bf..000000000
--- a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts
+++ /dev/null
@@ -1,39 +0,0 @@
1import { Component, Input } from '@angular/core'
2import { Account } from '../shared-main/account/account.model'
3
4@Component({
5 selector: 'my-account-avatar',
6 styleUrls: [ './account-avatar.component.scss' ],
7 templateUrl: './account-avatar.component.html'
8})
9export class AccountAvatarComponent {
10 @Input() account: {
11 name: string
12 avatar?: { url?: string, path: string }
13 url: string
14 }
15 @Input() size: '25' | '34' | '36' | '40' | '120' = '36'
16
17 // Use an external link
18 @Input() href: string
19 // Use routerLink
20 @Input() internalHref: string | string[]
21
22 @Input() set title (value) {
23 this._title = value
24 }
25
26 private _title: string
27
28 get title () {
29 return this._title || $localize`${this.account.name} (account page)`
30 }
31
32 get class () {
33 return `avatar avatar-${this.size}`
34 }
35
36 get avatarUrl () {
37 return Account.GET_ACTOR_AVATAR_URL(this.account)
38 }
39}
diff --git a/client/src/app/shared/shared-account-avatar/index.ts b/client/src/app/shared/shared-account-avatar/index.ts
deleted file mode 100644
index 40c742ba5..000000000
--- a/client/src/app/shared/shared-account-avatar/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
1export * from './account-avatar.component'
2export * from './shared-account-avatar.module' \ No newline at end of file
diff --git a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts b/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts
deleted file mode 100644
index 17b27589f..000000000
--- a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts
+++ /dev/null
@@ -1,23 +0,0 @@
1
2import { NgModule } from '@angular/core'
3import { SharedGlobalIconModule } from '../shared-icons'
4import { SharedMainModule } from '../shared-main/shared-main.module'
5import { AccountAvatarComponent } from './account-avatar.component'
6
7@NgModule({
8 imports: [
9 SharedMainModule,
10 SharedGlobalIconModule
11 ],
12
13 declarations: [
14 AccountAvatarComponent
15 ],
16
17 exports: [
18 AccountAvatarComponent
19 ],
20
21 providers: [ ]
22})
23export class SharedAccountAvatarModule { }
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
index 0829263f4..e9c5fadcf 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
@@ -1,6 +1,6 @@
1<div class="actor" *ngIf="actor"> 1<div class="actor" *ngIf="actor">
2 <div class="d-flex"> 2 <div class="d-flex">
3 <img [ngClass]="{ channel: isChannel() }" [src]="preview || actor.avatarUrl" alt="Avatar" /> 3 <my-actor-avatar [channel]="getChannel()" [account]="getAccount()" [previewImage]="preview" size="100"></my-actor-avatar>
4 4
5 <div class="actor-img-edit-container"> 5 <div class="actor-img-edit-container">
6 6
@@ -34,6 +34,7 @@
34 <span for="avatarfile" i18n>Upload a new avatar</span> 34 <span for="avatarfile" i18n>Upload a new avatar</span>
35 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> 35 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
36 </div> 36 </div>
37
37 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> 38 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
38 <my-global-icon iconName="delete"></my-global-icon> 39 <my-global-icon iconName="delete"></my-global-icon>
39 <span i18n>Remove avatar</span> 40 <span i18n>Remove avatar</span>
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
index 8b0172315..08e80c3b4 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
@@ -4,16 +4,8 @@
4.actor { 4.actor {
5 display: flex; 5 display: flex;
6 6
7 img { 7 my-actor-avatar {
8 margin-right: 15px; 8 margin-right: 15px;
9
10 &:not(.channel) {
11 @include avatar(100px);
12 }
13
14 &.channel {
15 @include channel-avatar(100px);
16 }
17 } 9 }
18 10
19 .actor-info { 11 .actor-info {
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
index d0d269489..840946690 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
@@ -80,4 +80,16 @@ export class ActorAvatarEditComponent implements OnInit {
80 isChannel () { 80 isChannel () {
81 return !!(this.actor as VideoChannel).ownerAccount 81 return !!(this.actor as VideoChannel).ownerAccount
82 } 82 }
83
84 getChannel (): VideoChannel {
85 if (this.isChannel()) return this.actor as VideoChannel
86
87 return undefined
88 }
89
90 getAccount (): Account {
91 if (this.isChannel()) return undefined
92
93 return this.actor as Account
94 }
83} 95}
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
index 266fc26c5..266fc26c5 100644
--- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
index 23606f871..23606f871 100644
--- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
index 8c12d3c4c..8c12d3c4c 100644
--- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
diff --git a/client/src/app/shared/shared-actor-image/actor-image-edit.scss b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
index 918955a89..918955a89 100644
--- a/client/src/app/shared/shared-actor-image/actor-image-edit.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
diff --git a/client/src/app/shared/shared-actor-image-edit/index.ts b/client/src/app/shared/shared-actor-image-edit/index.ts
new file mode 100644
index 000000000..276b2e2fb
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image-edit/index.ts
@@ -0,0 +1 @@
export * from './shared-actor-image-edit.module'
diff --git a/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts b/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts
new file mode 100644
index 000000000..f6a397d5c
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts
@@ -0,0 +1,31 @@
1
2import { CommonModule } from '@angular/common'
3import { NgModule } from '@angular/core'
4import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
5import { SharedGlobalIconModule } from '../shared-icons'
6import { SharedMainModule } from '../shared-main'
7import { ActorAvatarEditComponent } from './actor-avatar-edit.component'
8import { ActorBannerEditComponent } from './actor-banner-edit.component'
9
10@NgModule({
11 imports: [
12 CommonModule,
13
14 SharedMainModule,
15 SharedActorImageModule,
16 SharedGlobalIconModule
17 ],
18
19 declarations: [
20 ActorAvatarEditComponent,
21 ActorBannerEditComponent
22 ],
23
24 exports: [
25 ActorAvatarEditComponent,
26 ActorBannerEditComponent
27 ],
28
29 providers: [ ]
30})
31export class SharedActorImageEditModule { }
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.html b/client/src/app/shared/shared-actor-image/actor-avatar.component.html
new file mode 100644
index 000000000..13a5385a8
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.html
@@ -0,0 +1,19 @@
1<ng-template #img>
2 <img *ngIf="previewImage || avatarUrl || !initial" [class]="getClass('avatar')" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" />
3
4 <div *ngIf="!avatarUrl && initial" [class]="getClass('initial')">
5 <span>{{ initial }}</span>
6 </div>
7</ng-template>
8
9<a *ngIf="hasActor() && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
10 <ng-template *ngTemplateOutlet="img"></ng-template>
11</a>
12
13<a *ngIf="hasActor() && internalHref" [routerLink]="internalHref" [title]="title">
14 <ng-template *ngTemplateOutlet="img"></ng-template>
15</a>
16
17<ng-container *ngIf="!hasActor() || (!href && !internalHref)">
18 <ng-template *ngTemplateOutlet="img"></ng-template>
19</ng-container>
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.scss b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss
new file mode 100644
index 000000000..bf50de4e9
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss
@@ -0,0 +1,101 @@
1@import '_variables';
2@import '_mixins';
3
4.avatar {
5 --avatarSize: 100%;
6 --initialFontSize: 22px;
7
8 width: var(--avatarSize);
9 height: var(--avatarSize);
10 min-width: var(--avatarSize);
11 min-height: var(--avatarSize);
12
13 &.account {
14 object-fit: cover;
15 border-radius: 50%;
16 }
17
18 &.channel {
19 border-radius: 5px;
20 }
21}
22
23.avatar-18 {
24 --avatarSize: 18px;
25 --initialFontSize: 13px;
26}
27
28.avatar-25 {
29 --avatarSize: 25px;
30}
31
32.avatar-32 {
33 --avatarSize: 32px;
34}
35
36.avatar-34 {
37 --avatarSize: 34px;
38}
39
40.avatar-36 {
41 --avatarSize: 36px;
42}
43
44.avatar-40 {
45 --avatarSize: 40px;
46}
47
48.avatar-100 {
49 --avatarSize: 100px;
50 --initialFontSize: 40px;
51}
52
53.avatar-120 {
54 --avatarSize: 120px;
55 --initialFontSize: 46px;
56}
57
58a:hover {
59 text-decoration: none;
60}
61
62.initial {
63 background-color: #3C2109;
64 color: #fff;
65 display: flex;
66 align-items: center;
67 justify-content: center;
68 font-size: var(--initialFontSize);
69
70 &.blue {
71 background-color: #009FD4;
72 }
73
74 &.green {
75 background-color: #00AA55;
76 }
77
78 &.purple {
79 background-color: #B381B3;
80 }
81
82 &.gray {
83 background-color: #939393;
84 }
85
86 &.yellow {
87 background-color: #AA8F00;
88 }
89
90 &.orange {
91 background-color: #D47500;
92 }
93
94 &.red {
95 background-color: #E76E3C;
96 }
97
98 &.dark-blue {
99 background-color: #0A3055;
100 }
101}
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
new file mode 100644
index 000000000..b06c2bae6
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
@@ -0,0 +1,111 @@
1import { Component, Input } from '@angular/core'
2import { SafeResourceUrl } from '@angular/platform-browser'
3import { VideoChannel } from '../shared-main'
4import { Account } from '../shared-main/account/account.model'
5
6type ActorInput = {
7 name: string
8 avatar?: { url?: string, path: string }
9 url: string
10}
11
12export type ActorAvatarSize = '18' | '25' | '32' | '34' | '36' | '40' | '100' | '120'
13
14@Component({
15 selector: 'my-actor-avatar',
16 styleUrls: [ './actor-avatar.component.scss' ],
17 templateUrl: './actor-avatar.component.html'
18})
19export class ActorAvatarComponent {
20 @Input() account: ActorInput
21 @Input() channel: ActorInput
22
23 @Input() previewImage: SafeResourceUrl
24
25 @Input() size: ActorAvatarSize
26
27 // Use an external link
28 @Input() href: string
29 // Use routerLink
30 @Input() internalHref: string | any[]
31
32 @Input() set title (value) {
33 this._title = value
34 }
35
36 private _title: string
37
38 get title () {
39 if (this._title) return this._title
40 if (this.account) return $localize`${this.account.name} (account page)`
41 if (this.channel) return $localize`${this.channel.name} (channel page)`
42
43 return ''
44 }
45
46 get alt () {
47 if (this.account) return $localize`Account avatar`
48 if (this.channel) return $localize`Channel avatar`
49
50 return ''
51 }
52
53 getClass (type: 'avatar' | 'initial') {
54 const base = [ 'avatar' ]
55
56 if (this.size) base.push(`avatar-${this.size}`)
57
58 if (this.channel) base.push('channel')
59 else base.push('account')
60
61 if (type === 'initial' && this.initial) {
62 base.push('initial')
63 base.push(this.getColorTheme())
64 }
65
66 return base
67 }
68
69 get defaultAvatarUrl () {
70 if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL()
71
72 return Account.GET_DEFAULT_AVATAR_URL()
73 }
74
75 get avatarUrl () {
76 if (this.account) return Account.GET_ACTOR_AVATAR_URL(this.account)
77 if (this.channel) return VideoChannel.GET_ACTOR_AVATAR_URL(this.channel)
78
79 return ''
80 }
81
82 get initial () {
83 const name = this.account?.name
84 if (!name) return ''
85
86 return name.slice(0, 1)
87 }
88
89 hasActor () {
90 return !!this.account || !!this.channel
91 }
92
93 private getColorTheme () {
94 // Keep consistency with CSS
95 const themes = {
96 abc: 'blue',
97 def: 'green',
98 ghi: 'purple',
99 jkl: 'gray',
100 mno: 'yellow',
101 pqr: 'orange',
102 stvu: 'red',
103 wxyz: 'dark-blue'
104 }
105
106 const theme = Object.keys(themes)
107 .find(chars => chars.includes(this.initial))
108
109 return themes[theme]
110 }
111}
diff --git a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
index 6044f9925..8ea4bb2bf 100644
--- a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
+++ b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
@@ -1,27 +1,21 @@
1 1
2import { CommonModule } from '@angular/common'
3import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
4import { SharedGlobalIconModule } from '../shared-icons' 3import { SharedGlobalIconModule } from '../shared-icons'
5import { SharedMainModule } from '../shared-main' 4import { SharedMainModule } from '../shared-main/shared-main.module'
6import { ActorAvatarEditComponent } from './actor-avatar-edit.component' 5import { ActorAvatarComponent } from './actor-avatar.component'
7import { ActorBannerEditComponent } from './actor-banner-edit.component'
8 6
9@NgModule({ 7@NgModule({
10 imports: [ 8 imports: [
11 CommonModule,
12
13 SharedMainModule, 9 SharedMainModule,
14 SharedGlobalIconModule 10 SharedGlobalIconModule
15 ], 11 ],
16 12
17 declarations: [ 13 declarations: [
18 ActorAvatarEditComponent, 14 ActorAvatarComponent
19 ActorBannerEditComponent
20 ], 15 ],
21 16
22 exports: [ 17 exports: [
23 ActorAvatarEditComponent, 18 ActorAvatarComponent
24 ActorBannerEditComponent
25 ], 19 ],
26 20
27 providers: [ ] 21 providers: [ ]
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.html b/client/src/app/shared/shared-forms/advanced-input-filter.component.html
new file mode 100644
index 000000000..10d1296cf
--- /dev/null
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html
@@ -0,0 +1,24 @@
1<div class="input-group has-feedback has-clear">
2 <div *ngIf="hasFilters()" class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
3 <div class="input-group-text" ngbDropdownToggle>
4 <span class="caret" aria-haspopup="menu" role="button"></span>
5 </div>
6
7 <div role="menu" ngbDropdownMenu>
8 <h6 class="dropdown-header" i18n>Advanced filters</h6>
9
10 <a *ngFor="let filter of filters" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item">
11 {{ filter.label }}
12 </a>
13 </div>
14 </div>
15
16 <input
17 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
18 [(ngModel)]="searchValue"
19 (keyup)="onInputSearch($event)"
20 >
21
22 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a>
23 <span class="sr-only" i18n>Clear filters</span>
24</div>
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss
new file mode 100644
index 000000000..7c2198927
--- /dev/null
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss
@@ -0,0 +1,10 @@
1@import '_variables';
2@import '_mixins';
3
4input {
5 @include peertube-input-text(250px);
6}
7
8.input-group-text {
9 background-color: transparent;
10}
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts
new file mode 100644
index 000000000..c11f1ad1d
--- /dev/null
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts
@@ -0,0 +1,116 @@
1import * as debug from 'debug'
2import { Subject } from 'rxjs'
3import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
4import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
5import { ActivatedRoute, Params, Router } from '@angular/router'
6
7export type AdvancedInputFilter = {
8 label: string
9 queryParams: Params
10}
11
12const logger = debug('peertube:AdvancedInputFilterComponent')
13
14@Component({
15 selector: 'my-advanced-input-filter',
16 templateUrl: './advanced-input-filter.component.html',
17 styleUrls: [ './advanced-input-filter.component.scss' ]
18})
19export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
20 @Input() filters: AdvancedInputFilter[] = []
21
22 @Output() search = new EventEmitter<string>()
23
24 searchValue: string
25
26 private searchStream: Subject<string>
27
28 private viewInitialized = false
29 private emitSearchAfterViewInit = false
30
31 constructor (
32 private route: ActivatedRoute,
33 private router: Router
34 ) { }
35
36 ngOnInit () {
37 this.initSearchStream()
38 this.listenToRouteSearchChange()
39 }
40
41 ngAfterViewInit () {
42 this.viewInitialized = true
43
44 // Init after view init to not send an event too early
45 if (this.emitSearchAfterViewInit) this.emitSearch()
46 }
47
48 onInputSearch (event: Event) {
49 this.scheduleSearchUpdate((event.target as HTMLInputElement).value)
50 }
51
52 onResetTableFilter () {
53 this.immediateSearchUpdate('')
54 }
55
56 hasFilters () {
57 return this.filters.length !== 0
58 }
59
60 private scheduleSearchUpdate (value: string) {
61 this.searchValue = value
62 this.searchStream.next(this.searchValue)
63 }
64
65 private immediateSearchUpdate (value: string) {
66 this.searchValue = value
67
68 this.setQueryParams(this.searchValue)
69 this.emitSearch()
70 }
71
72 private listenToRouteSearchChange () {
73 this.route.queryParams
74 .subscribe(params => {
75 const search = params.search || ''
76
77 logger('On route search change "%s".', search)
78
79 this.searchValue = search
80 this.emitSearch()
81 })
82 }
83
84 private initSearchStream () {
85 this.searchStream = new Subject()
86
87 this.searchStream
88 .pipe(
89 debounceTime(300),
90 distinctUntilChanged()
91 )
92 .subscribe(() => {
93 this.setQueryParams(this.searchValue)
94
95 this.emitSearch()
96 })
97 }
98
99 private emitSearch () {
100 if (!this.viewInitialized) {
101 this.emitSearchAfterViewInit = true
102 return
103 }
104
105 logger('On search "%s".', this.searchValue)
106
107 this.search.emit(this.searchValue)
108 }
109
110 private setQueryParams (search: string) {
111 const queryParams: Params = {}
112
113 if (search) Object.assign(queryParams, { search })
114 this.router.navigate([ ], { queryParams })
115 }
116}
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts
index 1d859b991..727416a40 100644
--- a/client/src/app/shared/shared-forms/index.ts
+++ b/client/src/app/shared/shared-forms/index.ts
@@ -1,12 +1,14 @@
1export * from './form-validator.service' 1export * from './advanced-input-filter.component'
2export * from './form-reactive' 2export * from './form-reactive'
3export * from './select' 3export * from './form-validator.service'
4export * from './input-toggle-hidden.component' 4export * from './form-validator.service'
5export * from './input-switch.component' 5export * from './input-switch.component'
6export * from './input-toggle-hidden.component'
6export * from './markdown-textarea.component' 7export * from './markdown-textarea.component'
7export * from './peertube-checkbox.component' 8export * from './peertube-checkbox.component'
8export * from './preview-upload.component' 9export * from './preview-upload.component'
9export * from './reactive-file.component' 10export * from './reactive-file.component'
11export * from './select'
12export * from './shared-form.module'
10export * from './textarea-autoresize.directive' 13export * from './textarea-autoresize.directive'
11export * from './timestamp-input.component' 14export * from './timestamp-input.component'
12export * from './shared-form.module'
diff --git a/client/src/app/shared/shared-forms/input-switch.component.scss b/client/src/app/shared/shared-forms/input-switch.component.scss
index c14950bd7..290a70db8 100644
--- a/client/src/app/shared/shared-forms/input-switch.component.scss
+++ b/client/src/app/shared/shared-forms/input-switch.component.scss
@@ -5,7 +5,7 @@ input {
5 position: absolute; 5 position: absolute;
6 visibility: hidden; 6 visibility: hidden;
7 7
8 & + label { 8 + label {
9 cursor: pointer; 9 cursor: pointer;
10 text-indent: -9999px; 10 text-indent: -9999px;
11 width: 35px; 11 width: 35px;
@@ -16,7 +16,7 @@ input {
16 position: relative; 16 position: relative;
17 margin: 0; 17 margin: 0;
18 18
19 &:after { 19 &::after {
20 content: ''; 20 content: '';
21 position: absolute; 21 position: absolute;
22 top: 3px; 22 top: 3px;
@@ -28,7 +28,7 @@ input {
28 transition: 0.3s ease-out; 28 transition: 0.3s ease-out;
29 } 29 }
30 30
31 &:active:after { 31 &:active::after {
32 width: 40px; 32 width: 40px;
33 } 33 }
34 } 34 }
@@ -36,7 +36,7 @@ input {
36 &:checked + label { 36 &:checked + label {
37 background: pvar(--mainColor); 37 background: pvar(--mainColor);
38 38
39 &:after { 39 &::after {
40 left: calc(100% - 3px); 40 left: calc(100% - 3px);
41 transform: translateX(-100%); 41 transform: translateX(-100%);
42 } 42 }
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.scss b/client/src/app/shared/shared-forms/markdown-textarea.component.scss
index 8203c7d1c..1f72dbc32 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.scss
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.scss
@@ -18,7 +18,7 @@ $input-border-radius: 3px;
18 18
19 font-family: monospace; 19 font-family: monospace;
20 font-size: 13px; 20 font-size: 13px;
21 border-bottom: none; 21 border-bottom: 0;
22 border-bottom-left-radius: unset; 22 border-bottom-left-radius: unset;
23 border-bottom-right-radius: unset; 23 border-bottom-right-radius: unset;
24 } 24 }
@@ -51,7 +51,8 @@ $input-border-radius: 3px;
51 opacity: 0.6; 51 opacity: 0.6;
52 } 52 }
53 53
54 &:hover, &:active { 54 &:hover,
55 &:active {
55 svg { 56 svg {
56 opacity: 1; 57 opacity: 1;
57 } 58 }
@@ -105,6 +106,8 @@ $input-border-radius: 3px;
105} 106}
106 107
107@mixin maximized-base { 108@mixin maximized-base {
109 $nav-preview-vertical-padding: 40px;
110
108 flex-direction: row; 111 flex-direction: row;
109 z-index: #{z(header) - 1}; 112 z-index: #{z(header) - 1};
110 position: fixed; 113 position: fixed;
@@ -115,20 +118,18 @@ $input-border-radius: 3px;
115 width: calc(100% - #{$menu-width}); 118 width: calc(100% - #{$menu-width});
116 height: calc(100vh - #{$header-height}) !important; 119 height: calc(100vh - #{$header-height}) !important;
117 120
118 $nav-preview-vertical-padding: 40px;
119
120 .nav-preview { 121 .nav-preview {
121 @include nav-preview-medium(); 122 @include nav-preview-medium();
122 padding-top: #{$nav-preview-vertical-padding / 2}; 123 padding-top: #{$nav-preview-vertical-padding / 2};
123 padding-bottom: #{$nav-preview-vertical-padding / 2}; 124 padding-bottom: #{$nav-preview-vertical-padding / 2};
124 padding-left: 0px; 125 padding-left: 0;
125 padding-right: 0px; 126 padding-right: 0;
126 position: absolute; 127 position: absolute;
127 background-color: pvar(--mainBackgroundColor); 128 background-color: pvar(--mainBackgroundColor);
128 width: 100% !important; 129 width: 100% !important;
129 border-top: none; 130 border-top: 0;
130 border-left: none; 131 border-left: 0;
131 border-right: none; 132 border-right: 0;
132 133
133 :last-child { 134 :last-child {
134 margin-right: pvar(--horizontalMarginContent); 135 margin-right: pvar(--horizontalMarginContent);
@@ -148,7 +149,7 @@ $input-border-radius: 3px;
148 margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; 149 margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important;
149 height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; 150 height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important;
150 width: 50% !important; 151 width: 50% !important;
151 border: none !important; 152 border: 0 !important;
152 border-radius: unset !important; 153 border-radius: unset !important;
153 } 154 }
154 155
@@ -249,11 +250,11 @@ $input-border-radius: 3px;
249} 250}
250 251
251@media only screen and (min-width: $small-view) { 252@media only screen and (min-width: $small-view) {
253 @include maximized-in-medium-view();
254
252 :host-context(.expanded) { 255 :host-context(.expanded) {
253 @include in-medium-view(); 256 @include in-medium-view();
254 } 257 }
255
256 @include maximized-in-medium-view();
257} 258}
258 259
259@media only screen and (min-width: #{$small-view + $menu-width}) { 260@media only screen and (min-width: #{$small-view + $menu-width}) {
diff --git a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
index cf8540dc3..203b82d0b 100644
--- a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
+++ b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
@@ -46,7 +46,7 @@
46 line-height: 12px; 46 line-height: 12px;
47 font-weight: 500; 47 font-weight: 500;
48 color: pvar(--inputPlaceholderColor); 48 color: pvar(--inputPlaceholderColor);
49 background-color: rgba(217,225,232,.1); 49 background-color: rgba(217, 225, 232, .1);
50 border: 1px solid rgba(217,225,232,.5); 50 border: 1px solid rgba(217, 225, 232, .5);
51 } 51 }
52} \ No newline at end of file 52}
diff --git a/client/src/app/shared/shared-forms/preview-upload.component.scss b/client/src/app/shared/shared-forms/preview-upload.component.scss
index 88eccd5f7..c2ee0d6a9 100644
--- a/client/src/app/shared/shared-forms/preview-upload.component.scss
+++ b/client/src/app/shared/shared-forms/preview-upload.component.scss
@@ -21,7 +21,7 @@
21 max-width: 100%; 21 max-width: 100%;
22 22
23 &.no-image { 23 &.no-image {
24 border: 2px solid grey; 24 border: 2px solid #808080;
25 background-color: pvar(--mainBackgroundColor); 25 background-color: pvar(--mainBackgroundColor);
26 } 26 }
27 } 27 }
diff --git a/client/src/app/shared/shared-forms/select/select-shared.component.scss b/client/src/app/shared/shared-forms/select/select-shared.component.scss
index 80196b8df..7006adab1 100644
--- a/client/src/app/shared/shared-forms/select/select-shared.component.scss
+++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss
@@ -32,7 +32,7 @@ ng-select ::ng-deep {
32} 32}
33 33
34.root { 34.root {
35 display:flex; 35 display: flex;
36 align-items: center; 36 align-items: center;
37 37
38 > my-select-options { 38 > my-select-options {
@@ -41,9 +41,9 @@ ng-select ::ng-deep {
41} 41}
42 42
43my-select-options + input { 43my-select-options + input {
44 margin-left: 5px;
45
46 @include peertube-input-text($form-base-input-width); 44 @include peertube-input-text($form-base-input-width);
45
46 margin-left: 5px;
47 display: block; 47 display: block;
48} 48}
49 49
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts
index 9bdd138a1..5417f7342 100644
--- a/client/src/app/shared/shared-forms/shared-form.module.ts
+++ b/client/src/app/shared/shared-forms/shared-form.module.ts
@@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'
5import { NgSelectModule } from '@ng-select/ng-select' 5import { NgSelectModule } from '@ng-select/ng-select'
6import { SharedGlobalIconModule } from '../shared-icons' 6import { SharedGlobalIconModule } from '../shared-icons'
7import { SharedMainModule } from '../shared-main/shared-main.module' 7import { SharedMainModule } from '../shared-main/shared-main.module'
8import { AdvancedInputFilterComponent } from './advanced-input-filter.component'
8import { DynamicFormFieldComponent } from './dynamic-form-field.component' 9import { DynamicFormFieldComponent } from './dynamic-form-field.component'
9import { FormValidatorService } from './form-validator.service' 10import { FormValidatorService } from './form-validator.service'
10import { InputSwitchComponent } from './input-switch.component' 11import { InputSwitchComponent } from './input-switch.component'
@@ -52,7 +53,9 @@ import { TimestampInputComponent } from './timestamp-input.component'
52 SelectCheckboxComponent, 53 SelectCheckboxComponent,
53 SelectCustomValueComponent, 54 SelectCustomValueComponent,
54 55
55 DynamicFormFieldComponent 56 DynamicFormFieldComponent,
57
58 AdvancedInputFilterComponent
56 ], 59 ],
57 60
58 exports: [ 61 exports: [
@@ -78,7 +81,9 @@ import { TimestampInputComponent } from './timestamp-input.component'
78 SelectCheckboxComponent, 81 SelectCheckboxComponent,
79 SelectCustomValueComponent, 82 SelectCustomValueComponent,
80 83
81 DynamicFormFieldComponent 84 DynamicFormFieldComponent,
85
86 AdvancedInputFilterComponent
82 ], 87 ],
83 88
84 providers: [ 89 providers: [
diff --git a/client/src/app/shared/shared-forms/timestamp-input.component.scss b/client/src/app/shared/shared-forms/timestamp-input.component.scss
index 66e9aa032..36f5711a6 100644
--- a/client/src/app/shared/shared-forms/timestamp-input.component.scss
+++ b/client/src/app/shared/shared-forms/timestamp-input.component.scss
@@ -4,8 +4,7 @@ p-inputmask {
4 ::ng-deep input { 4 ::ng-deep input {
5 width: 80px; 5 width: 80px;
6 font-size: 15px; 6 font-size: 15px;
7 7 border: 0;
8 border: none;
9 8
10 &:focus-within, 9 &:focus-within,
11 &:focus { 10 &:focus {
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss
index 2f6b420e3..615e08bcc 100644
--- a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss
+++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss
@@ -1,6 +1,6 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3@import "./_bootstrap-variables"; 3@import './_bootstrap-variables';
4 4
5@import '~bootstrap/scss/functions'; 5@import '~bootstrap/scss/functions';
6@import '~bootstrap/scss/variables'; 6@import '~bootstrap/scss/variables';
@@ -30,7 +30,7 @@ ngb-accordion ::ng-deep {
30 background-color: unset; 30 background-color: unset;
31 padding: 0; 31 padding: 0;
32 32
33 & + .collapse.show { 33 + .collapse.show {
34 background-color: var(--submenuBackgroundColor); 34 background-color: var(--submenuBackgroundColor);
35 } 35 }
36 } 36 }
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.scss b/client/src/app/shared/shared-instance/instance-features-table.component.scss
index d17e91fc2..11cf11616 100644
--- a/client/src/app/shared/shared-instance/instance-features-table.component.scss
+++ b/client/src/app/shared/shared-instance/instance-features-table.component.scss
@@ -19,7 +19,7 @@ table {
19 .more-info { 19 .more-info {
20 font-style: italic; 20 font-style: italic;
21 font-weight: initial; 21 font-weight: initial;
22 font-size: 14px 22 font-size: 14px;
23 } 23 }
24 } 24 }
25 25
diff --git a/client/src/app/shared/shared-main/account/account.model.ts b/client/src/app/shared/shared-main/account/account.model.ts
index 65e6798d4..6d9f0ee65 100644
--- a/client/src/app/shared/shared-main/account/account.model.ts
+++ b/client/src/app/shared/shared-main/account/account.model.ts
@@ -14,7 +14,7 @@ export class Account extends Actor implements ServerAccount {
14 userId?: number 14 userId?: number
15 15
16 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) { 16 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) {
17 return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() 17 return Actor.GET_ACTOR_AVATAR_URL(actor)
18 } 18 }
19 19
20 static GET_DEFAULT_AVATAR_URL () { 20 static GET_DEFAULT_AVATAR_URL () {
@@ -24,8 +24,6 @@ export class Account extends Actor implements ServerAccount {
24 constructor (hash: ServerAccount) { 24 constructor (hash: ServerAccount) {
25 super(hash) 25 super(hash)
26 26
27 this.updateComputedAttributes()
28
29 this.displayName = hash.displayName 27 this.displayName = hash.displayName
30 this.description = hash.description 28 this.description = hash.description
31 this.userId = hash.userId 29 this.userId = hash.userId
@@ -40,16 +38,9 @@ export class Account extends Actor implements ServerAccount {
40 38
41 updateAvatar (newAvatar: ActorImage) { 39 updateAvatar (newAvatar: ActorImage) {
42 this.avatar = newAvatar 40 this.avatar = newAvatar
43
44 this.updateComputedAttributes()
45 } 41 }
46 42
47 resetAvatar () { 43 resetAvatar () {
48 this.avatar = null 44 this.avatar = null
49 this.avatarUrl = Account.GET_DEFAULT_AVATAR_URL()
50 }
51
52 private updateComputedAttributes () {
53 this.avatarUrl = Account.GET_ACTOR_AVATAR_URL(this)
54 } 45 }
55} 46}
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts
index 4b036341f..6ba0bb09e 100644
--- a/client/src/app/shared/shared-main/account/actor.model.ts
+++ b/client/src/app/shared/shared-main/account/actor.model.ts
@@ -15,7 +15,6 @@ export abstract class Actor implements ServerActor {
15 updatedAt: Date | string 15 updatedAt: Date | string
16 16
17 avatar: ActorImage 17 avatar: ActorImage
18 avatarUrl: string
19 18
20 isLocal: boolean 19 isLocal: boolean
21 20
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss b/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss
index 724a04efc..b9a4d46dc 100644
--- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss
+++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss
@@ -8,6 +8,9 @@
8.action-button { 8.action-button {
9 @include peertube-button; 9 @include peertube-button;
10 10
11 display: inline-block;
12 padding: 0 10px;
13
11 &.button-styled { 14 &.button-styled {
12 15
13 &.grey { 16 &.grey {
@@ -18,14 +21,13 @@
18 @include orange-button; 21 @include orange-button;
19 } 22 }
20 23
21 &:hover, &:active, &:focus { 24 &:hover,
25 &:active,
26 &:focus {
22 background-color: $grey-background-color; 27 background-color: $grey-background-color;
23 } 28 }
24 } 29 }
25 30
26 display: inline-block;
27 padding: 0 10px;
28
29 &::after { 31 &::after {
30 display: none; 32 display: none;
31 } 33 }
@@ -64,7 +66,8 @@
64 @include dropdown-with-icon-item; 66 @include dropdown-with-icon-item;
65 } 67 }
66 68
67 a, span { 69 a,
70 span {
68 display: block; 71 display: block;
69 width: 100%; 72 width: 100%;
70 } 73 }
diff --git a/client/src/app/shared/shared-main/buttons/button.component.scss b/client/src/app/shared/shared-main/buttons/button.component.scss
index f73b7b808..09b5f95d7 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.scss
+++ b/client/src/app/shared/shared-main/buttons/button.component.scss
@@ -1,6 +1,16 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4@mixin responsive-label {
5 .action-button {
6 padding: 0 13px;
7 }
8
9 .button-label {
10 display: none;
11 }
12}
13
4:host { 14:host {
5 outline: none; 15 outline: none;
6} 16}
@@ -46,12 +56,12 @@ span[class$=-button] {
46// In a table, try to minimize the space taken by this button 56// In a table, try to minimize the space taken by this button
47@media screen and (max-width: 1400px) { 57@media screen and (max-width: 1400px) {
48 :host-context(td) { 58 :host-context(td) {
49 .action-button { 59 @include responsive-label;
50 padding: 0 13px; 60 }
51 } 61}
52 62
53 .button-label { 63@media screen and (max-width: $small-view) {
54 display: none; 64 .responsive-label {
55 } 65 @include responsive-label;
56 } 66 }
57} 67}
diff --git a/client/src/app/shared/shared-main/buttons/button.component.ts b/client/src/app/shared/shared-main/buttons/button.component.ts
index 1d2be0bf9..ee74b3d12 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/button.component.ts
@@ -3,7 +3,7 @@ import { GlobalIconName } from '@app/shared/shared-icons'
3 3
4@Component({ 4@Component({
5 selector: 'my-button', 5 selector: 'my-button',
6 styleUrls: ['./button.component.scss'], 6 styleUrls: [ './button.component.scss' ],
7 templateUrl: './button.component.html' 7 templateUrl: './button.component.html'
8}) 8})
9 9
@@ -14,6 +14,7 @@ export class ButtonComponent {
14 @Input() title: string = undefined 14 @Input() title: string = undefined
15 @Input() loading = false 15 @Input() loading = false
16 @Input() disabled = false 16 @Input() disabled = false
17 @Input() responsiveLabel = false
17 18
18 getTitle () { 19 getTitle () {
19 return this.title || this.label 20 return this.title || this.label
@@ -22,7 +23,8 @@ export class ButtonComponent {
22 getClasses () { 23 getClasses () {
23 return { 24 return {
24 [this.className]: true, 25 [this.className]: true,
25 disabled: this.disabled 26 disabled: this.disabled,
27 'responsive-label': this.responsiveLabel
26 } 28 }
27 } 29 }
28} 30}
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.html b/client/src/app/shared/shared-main/buttons/delete-button.component.html
index c94d8d0c9..d7a6702a7 100644
--- a/client/src/app/shared/shared-main/buttons/delete-button.component.html
+++ b/client/src/app/shared/shared-main/buttons/delete-button.component.html
@@ -1,4 +1,7 @@
1<span class="action-button action-button-delete grey-button" [ngbTooltip]="title" role="button" tabindex="0"> 1<span
2 class="action-button action-button-delete grey-button"
3 [ngClass]="{ 'responsive-label': responsiveLabel }" [ngbTooltip]="title" role="button" tabindex="0"
4>
2 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon> 5 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
3 6
4 <span class="button-label" *ngIf="label">{{ label }}</span> 7 <span class="button-label" *ngIf="label">{{ label }}</span>
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.ts b/client/src/app/shared/shared-main/buttons/delete-button.component.ts
index 18995422a..c091f5309 100644
--- a/client/src/app/shared/shared-main/buttons/delete-button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/delete-button.component.ts
@@ -9,6 +9,7 @@ import { Component, Input, OnInit } from '@angular/core'
9export class DeleteButtonComponent implements OnInit { 9export class DeleteButtonComponent implements OnInit {
10 @Input() label: string 10 @Input() label: string
11 @Input() title: string 11 @Input() title: string
12 @Input() responsiveLabel = false
12 13
13 ngOnInit () { 14 ngOnInit () {
14 // <my-delete-button /> No label 15 // <my-delete-button /> No label
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.html b/client/src/app/shared/shared-main/buttons/edit-button.component.html
index ecb709be1..8beeee6c4 100644
--- a/client/src/app/shared/shared-main/buttons/edit-button.component.html
+++ b/client/src/app/shared/shared-main/buttons/edit-button.component.html
@@ -1,4 +1,7 @@
1<a class="action-button action-button-edit grey-button" [routerLink]="routerLink" [ngbTooltip]="title"> 1<a
2 class="action-button action-button-edit grey-button"
3 [ngClass]="{ 'responsive-label': responsiveLabel }" [routerLink]="routerLink" [ngbTooltip]="title"
4>
2 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon> 5 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon>
3 6
4 <span class="button-label" *ngIf="label">{{ label }}</span> 7 <span class="button-label" *ngIf="label">{{ label }}</span>
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.ts b/client/src/app/shared/shared-main/buttons/edit-button.component.ts
index 4b76551ca..24c8625ff 100644
--- a/client/src/app/shared/shared-main/buttons/edit-button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/edit-button.component.ts
@@ -5,11 +5,11 @@ import { Component, Input, OnInit } from '@angular/core'
5 styleUrls: [ './button.component.scss' ], 5 styleUrls: [ './button.component.scss' ],
6 templateUrl: './edit-button.component.html' 6 templateUrl: './edit-button.component.html'
7}) 7})
8
9export class EditButtonComponent implements OnInit { 8export class EditButtonComponent implements OnInit {
10 @Input() label: string 9 @Input() label: string
11 @Input() title: string 10 @Input() title: string
12 @Input() routerLink: string[] | string = [] 11 @Input() routerLink: string[] | string = []
12 @Input() responsiveLabel = false
13 13
14 ngOnInit () { 14 ngOnInit () {
15 // <my-edit-button /> No label 15 // <my-edit-button /> No label
diff --git a/client/src/app/shared/shared-main/date/date-toggle.component.scss b/client/src/app/shared/shared-main/date/date-toggle.component.scss
index 86700d1d4..b87f7c475 100644
--- a/client/src/app/shared/shared-main/date/date-toggle.component.scss
+++ b/client/src/app/shared/shared-main/date/date-toggle.component.scss
@@ -1,5 +1,5 @@
1.date-toggle { 1.date-toggle {
2 &:hover { 2 &:hover {
3 cursor: default 3 cursor: default;
4 } 4 }
5} 5}
diff --git a/client/src/app/shared/shared-main/feeds/feed.component.scss b/client/src/app/shared/shared-main/feeds/feed.component.scss
index b655ee708..d39f31d70 100644
--- a/client/src/app/shared/shared-main/feeds/feed.component.scss
+++ b/client/src/app/shared/shared-main/feeds/feed.component.scss
@@ -5,14 +5,14 @@
5 width: 100%; 5 width: 100%;
6 6
7 a { 7 a {
8 color: black; 8 color: #000;
9 display: block; 9 display: block;
10 } 10 }
11} 11}
12 12
13my-global-icon { 13my-global-icon {
14 @include apply-svg-color(pvar(--mainForegroundColor));
15
14 cursor: pointer; 16 cursor: pointer;
15 width: 100%; 17 width: 100%;
16
17 @include apply-svg-color(pvar(--mainForegroundColor))
18} 18}
diff --git a/client/src/app/shared/shared-main/loaders/loader.component.scss b/client/src/app/shared/shared-main/loaders/loader.component.scss
index ffac9c707..64138afe4 100644
--- a/client/src/app/shared/shared-main/loaders/loader.component.scss
+++ b/client/src/app/shared/shared-main/loaders/loader.component.scss
@@ -20,7 +20,7 @@
20 border: 4px solid; 20 border: 4px solid;
21 border-radius: 50%; 21 border-radius: 50%;
22 animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 22 animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
23 border-color: #999999 transparent transparent transparent; 23 border-color: #999999 transparent transparent;
24} 24}
25 25
26.loader div:nth-child(1) { 26.loader div:nth-child(1) {
diff --git a/client/src/app/shared/shared-main/misc/help.component.scss b/client/src/app/shared/shared-main/misc/help.component.scss
index ccc91ffab..68d7ad48f 100644
--- a/client/src/app/shared/shared-main/misc/help.component.scss
+++ b/client/src/app/shared/shared-main/misc/help.component.scss
@@ -2,20 +2,19 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.help-tooltip-button { 4.help-tooltip-button {
5 cursor: pointer; 5 @include disable-outline;
6 border: none;
7 6
7 cursor: pointer;
8 border: 0;
8 margin: 5px; 9 margin: 5px;
9 10
10 my-global-icon { 11 my-global-icon {
12 @include apply-svg-color(pvar(--greyForegroundColor));
13
11 width: 17px; 14 width: 17px;
12 position: relative; 15 position: relative;
13 top: -1px; 16 top: -1px;
14
15 @include apply-svg-color(pvar(--greyForegroundColor))
16 } 17 }
17
18 @include disable-outline;
19} 18}
20 19
21::ng-deep { 20::ng-deep {
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.html b/client/src/app/shared/shared-main/misc/list-overflow.component.html
index 986572801..b2e0982f1 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.html
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.html
@@ -2,19 +2,19 @@
2 <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id"> 2 <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id">
3 <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container> 3 <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container>
4 </span> 4 </span>
5 5
6 <ng-container *ngIf="isMenuDisplayed()"> 6 <ng-container *ngIf="isMenuDisplayed()">
7 <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()"> 7 <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()">
8 <span class="glyphicon glyphicon-chevron-down"></span> 8 <span class="glyphicon glyphicon-chevron-down"></span>
9 </button> 9 </button>
10 10
11 <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)"> 11 <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)">
12 <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ routeActive: active }" 12 <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ 'route-active': active }"
13 ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" 13 ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button"
14 > 14 >
15 <span class="glyphicon glyphicon-chevron-down"></span> 15 <span class="glyphicon glyphicon-chevron-down"></span>
16 </button> 16 </button>
17 17
18 <div ngbDropdownMenu> 18 <div ngbDropdownMenu>
19 <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length" 19 <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length"
20 [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item"> 20 [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item">
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.scss b/client/src/app/shared/shared-main/misc/list-overflow.component.scss
index 1ec044489..7e31d3850 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.scss
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.scss
@@ -15,13 +15,13 @@
15 15
16button { 16button {
17 width: 30px; 17 width: 30px;
18 border: none; 18 border: 0;
19 19
20 &::after { 20 &::after {
21 display: none; 21 display: none;
22 } 22 }
23 23
24 &.routeActive { 24 &.route-active {
25 &::after { 25 &::after {
26 display: inherit; 26 display: inherit;
27 border: 2px solid pvar(--mainColor); 27 border: 2px solid pvar(--mainColor);
@@ -36,7 +36,7 @@ button {
36 margin-top: 0 !important; 36 margin-top: 0 !important;
37 position: static; 37 position: static;
38 right: auto; 38 right: auto;
39 bottom: auto 39 bottom: auto;
40} 40}
41 41
42.modal-body { 42.modal-body {
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss
index 84dd7dce3..ffabb3646 100644
--- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss
+++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss
@@ -11,12 +11,12 @@
11 } 11 }
12} 12}
13 13
14::ng-deep .dropdown-toggle::after { 14.sub-menu ::ng-deep .dropdown-toggle::after {
15 position: relative; 15 position: relative;
16 top: 2px; 16 top: 2px;
17} 17}
18 18
19::ng-deep .dropdown-menu { 19.sub-menu ::ng-deep .dropdown-menu {
20 margin-top: 0 !important; 20 margin-top: 0 !important;
21} 21}
22 22
diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts
index 88a4811da..ed5791794 100644
--- a/client/src/app/shared/shared-main/users/user-notification.model.ts
+++ b/client/src/app/shared/shared-main/users/user-notification.model.ts
@@ -258,10 +258,10 @@ export class UserNotification implements UserNotificationServer {
258 } 258 }
259 259
260 private setAccountAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { 260 private setAccountAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) {
261 actor.avatarUrl = Account.GET_ACTOR_AVATAR_URL(actor) 261 actor.avatarUrl = Account.GET_ACTOR_AVATAR_URL(actor) || Account.GET_DEFAULT_AVATAR_URL()
262 } 262 }
263 263
264 private setVideoChannelAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { 264 private setVideoChannelAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) {
265 actor.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(actor) 265 actor.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(actor) || VideoChannel.GET_DEFAULT_AVATAR_URL()
266 } 266 }
267} 267}
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.scss b/client/src/app/shared/shared-main/users/user-notifications.component.scss
index 5166bd559..b69d4b5d6 100644
--- a/client/src/app/shared/shared-main/users/user-notifications.component.scss
+++ b/client/src/app/shared/shared-main/users/user-notifications.component.scss
@@ -21,16 +21,19 @@
21 } 21 }
22 22
23 my-global-icon { 23 my-global-icon {
24 @include apply-svg-color(#333);
25
24 width: 24px; 26 width: 24px;
25 margin-right: 11px; 27 margin-right: 11px;
26 margin-left: 3px; 28 margin-left: 3px;
27
28 @include apply-svg-color(#333);
29 } 29 }
30 30
31 .avatar { 31 .avatar {
32 @include avatar(30px); 32 width: 30px;
33 33 height: 30px;
34 min-width: 30px;
35 min-height: 30px;
36 border-radius: 5px;
34 margin-right: 10px; 37 margin-right: 10px;
35 } 38 }
36 39
diff --git a/client/src/app/shared/shared-main/users/user-quota.component.scss b/client/src/app/shared/shared-main/users/user-quota.component.scss
index c670559d3..c06cafe29 100644
--- a/client/src/app/shared/shared-main/users/user-quota.component.scss
+++ b/client/src/app/shared/shared-main/users/user-quota.component.scss
@@ -11,7 +11,8 @@ label {
11 margin-right: 5px; 11 margin-right: 5px;
12 } 12 }
13 13
14 &, .progress { 14 &,
15 .progress {
15 width: 100% !important; 16 width: 100% !important;
16 } 17 }
17 18
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
index 1ba3fcc0e..c40dd5311 100644
--- a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
+++ b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
@@ -18,14 +18,13 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
18 18
19 ownerAccount?: ServerAccount 19 ownerAccount?: ServerAccount
20 ownerBy?: string 20 ownerBy?: string
21 ownerAvatarUrl?: string
22 21
23 videosCount?: number 22 videosCount?: number
24 23
25 viewsPerDay?: ViewsPerDate[] 24 viewsPerDay?: ViewsPerDate[]
26 25
27 static GET_ACTOR_AVATAR_URL (actor: object) { 26 static GET_ACTOR_AVATAR_URL (actor: object) {
28 return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() 27 return Actor.GET_ACTOR_AVATAR_URL(actor)
29 } 28 }
30 29
31 static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) { 30 static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) {
@@ -67,7 +66,6 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
67 if (hash.ownerAccount) { 66 if (hash.ownerAccount) {
68 this.ownerAccount = hash.ownerAccount 67 this.ownerAccount = hash.ownerAccount
69 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) 68 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host)
70 this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount)
71 } 69 }
72 70
73 this.updateComputedAttributes() 71 this.updateComputedAttributes()
@@ -94,7 +92,6 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
94 } 92 }
95 93
96 updateComputedAttributes () { 94 updateComputedAttributes () {
97 this.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this)
98 this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this) 95 this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this)
99 } 96 }
100} 97}
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index 14c507295..526d10e32 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -20,8 +20,6 @@ export class Video implements VideoServerModel {
20 byVideoChannel: string 20 byVideoChannel: string
21 byAccount: string 21 byAccount: string
22 22
23 videoChannelAvatarUrl: string
24
25 createdAt: Date 23 createdAt: Date
26 updatedAt: Date 24 updatedAt: Date
27 publishedAt: Date 25 publishedAt: Date
@@ -143,7 +141,6 @@ export class Video implements VideoServerModel {
143 141
144 this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) 142 this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host)
145 this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host) 143 this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host)
146 this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.channel)
147 144
148 this.category.label = peertubeTranslate(this.category.label, translations) 145 this.category.label = peertubeTranslate(this.category.label, translations)
149 this.licence.label = peertubeTranslate(this.licence.label, translations) 146 this.licence.label = peertubeTranslate(this.licence.label, translations)
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts
index 0b708b692..7b17bd2ab 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -124,7 +124,17 @@ export class VideoService implements VideosProvider {
124 124
125 let params = new HttpParams() 125 let params = new HttpParams()
126 params = this.restService.addRestGetParams(params, pagination, sort) 126 params = this.restService.addRestGetParams(params, pagination, sort)
127 params = this.restService.addObjectParams(params, { search }) 127
128 if (search) {
129 const filters = this.restService.parseQueryStringFilter(search, {
130 isLive: {
131 prefix: 'isLive:',
132 isBoolean: true
133 }
134 })
135
136 params = this.restService.addObjectParams(params, filters)
137 }
128 138
129 return this.authHttp 139 return this.authHttp
130 .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params }) 140 .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params })
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.html b/client/src/app/shared/shared-moderation/account-blocklist.component.html
index 3f2f55559..a9fac0810 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.html
@@ -11,13 +11,8 @@
11> 11>
12 <ng-template pTemplate="caption"> 12 <ng-template pTemplate="caption">
13 <div class="caption"> 13 <div class="caption">
14 <div class="ml-auto has-feedback has-clear"> 14 <div class="ml-auto">
15 <input 15 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
16 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
17 (keyup)="onSearch($event)"
18 >
19 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
20 <span class="sr-only" i18n>Clear filters</span>
21 </div> 16 </div>
22 </div> 17 </div>
23 </ng-template> 18 </ng-template>
@@ -38,7 +33,7 @@
38 <td> 33 <td>
39 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 34 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
40 <div class="chip two-lines"> 35 <div class="chip two-lines">
41 <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar> 36 <my-actor-avatar [account]="accountBlock.blockedAccount"></my-actor-avatar>
42 <div> 37 <div>
43 {{ accountBlock.blockedAccount.displayName }} 38 {{ accountBlock.blockedAccount.displayName }}
44 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span> 39 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.scss b/client/src/app/shared/shared-moderation/account-blocklist.component.scss
index 3eede44eb..bc441811e 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.scss
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.scss
@@ -1,15 +1,6 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.caption {
5 justify-content: flex-end;
6
7 input {
8 @include peertube-input-text(250px);
9 flex-grow: 1;
10 }
11}
12
13.chip { 4.chip {
14 @include chip; 5 @include chip;
15} 6}
@@ -17,4 +8,4 @@
17.unblock-button { 8.unblock-button {
18 @include peertube-button; 9 @include peertube-button;
19 @include grey-button; 10 @include grey-button;
20} \ No newline at end of file 11}
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.ts b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
index 1bce65bf0..1146aeec0 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
@@ -44,12 +44,12 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
44 : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.` 44 : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.`
45 ) 45 )
46 46
47 this.loadData() 47 this.reloadData()
48 } 48 }
49 ) 49 )
50 } 50 }
51 51
52 protected loadData () { 52 protected reloadData () {
53 const operation = this.mode === BlocklistComponentType.Account 53 const operation = this.mode === BlocklistComponentType.Account
54 ? this.blocklistService.getUserAccountBlocklist({ 54 ? this.blocklistService.getUserAccountBlocklist({
55 pagination: this.pagination, 55 pagination: this.pagination,
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss
index cdcc12fe0..b13d06f03 100644
--- a/client/src/app/shared/shared-moderation/moderation.scss
+++ b/client/src/app/shared/shared-moderation/moderation.scss
@@ -17,33 +17,25 @@
17 word-wrap: break-word; 17 word-wrap: break-word;
18 18
19 ::ng-deep p:last-child { 19 ::ng-deep p:last-child {
20 margin-bottom: 0px !important; 20 margin-bottom: 0 !important;
21 } 21 }
22 } 22 }
23} 23}
24 24
25.screenratio { 25.screenratio {
26 div {
27 @include miniature-thumbnail;
28
29 display: inline-flex;
30 justify-content: center;
31 align-items: center;
32 color: pvar(--inputPlaceholderColor);
33 }
34
35 @include block-ratio($selector: 'div, ::ng-deep iframe') { 26 @include block-ratio($selector: 'div, ::ng-deep iframe') {
36 width: 100% !important; 27 width: 100% !important;
37 height: 100% !important; 28 height: 100% !important;
38 left: 0; 29 left: 0;
39 }; 30 };
40}
41 31
42.input-group { 32 div {
43 @include peertube-input-group(300px); 33 @include miniature-thumbnail;
44 34
45 .dropdown-toggle::after { 35 display: inline-flex;
46 margin-left: 0; 36 justify-content: center;
37 align-items: center;
38 color: pvar(--inputPlaceholderColor);
47 } 39 }
48} 40}
49 41
@@ -51,15 +43,6 @@
51 @include chip; 43 @include chip;
52} 44}
53 45
54.caption {
55 justify-content: flex-end;
56
57 input {
58 @include peertube-input-text(250px);
59 flex-grow: 1;
60 }
61}
62
63my-action-dropdown.show { 46my-action-dropdown.show {
64 ::ng-deep .dropdown-root { 47 ::ng-deep .dropdown-root {
65 display: block !important; 48 display: block !important;
@@ -93,15 +76,15 @@ my-action-dropdown.show {
93 display: inline-flex; 76 display: inline-flex;
94 77
95 .table-video-image { 78 .table-video-image {
96 @include miniature-thumbnail;
97
98 $image-height: 45px; 79 $image-height: 45px;
99 80
81 @include miniature-thumbnail;
82
100 height: $image-height; 83 height: $image-height;
101 width: #{(16/9) * $image-height}; 84 width: #{(16/9) * $image-height};
102 margin-right: 0.5rem; 85 margin-right: 0.5rem;
103 border-radius: 2px; 86 border-radius: 2px;
104 border: none; 87 border: 0;
105 background: transparent; 88 background: transparent;
106 display: inline-flex; 89 display: inline-flex;
107 justify-content: center; 90 justify-content: center;
@@ -139,7 +122,7 @@ my-action-dropdown.show {
139 122
140 div .glyphicon { 123 div .glyphicon {
141 font-size: 80%; 124 font-size: 80%;
142 color: gray; 125 color: #808080;
143 margin-left: 0.1rem; 126 margin-left: 0.1rem;
144 } 127 }
145 128
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.html b/client/src/app/shared/shared-moderation/server-blocklist.component.html
index 537186f05..c6d29bb21 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html
@@ -4,8 +4,9 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="blockedServers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="blockedServers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 8 [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances"
11> 12>
@@ -18,13 +19,8 @@
18 </a> 19 </a>
19 </div> 20 </div>
20 21
21 <div class="ml-auto has-feedback has-clear"> 22 <div class="ml-auto">
22 <input 23 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
23 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
24 (keyup)="onSearch($event)"
25 >
26 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
27 <span class="sr-only" i18n>Clear filters</span>
28 </div> 24 </div>
29 </div> 25 </div>
30 </ng-template> 26 </ng-template>
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.scss b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
index 31db4d92b..a22972c5f 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.scss
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
@@ -5,7 +5,8 @@ a {
5 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
6 display: inline-block; 6 display: inline-block;
7 7
8 &, &:hover { 8 &,
9 &:hover {
9 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
10 } 11 }
11 12
@@ -15,15 +16,6 @@ a {
15 } 16 }
16} 17}
17 18
18.caption {
19 justify-content: flex-end;
20
21 input {
22 @include peertube-input-text(250px);
23 flex-grow: 1;
24 }
25}
26
27.unblock-button { 19.unblock-button {
28 @include peertube-button; 20 @include peertube-button;
29 @include grey-button; 21 @include grey-button;
@@ -33,15 +25,6 @@ a {
33 @include create-button; 25 @include create-button;
34} 26}
35 27
36.caption {
37 justify-content: flex-end;
38
39 input {
40 @include peertube-input-text(250px);
41 flex-grow: 1;
42 }
43}
44
45.chip { 28.chip {
46 @include chip; 29 @include chip;
47} 30}
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.ts b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
index 546fd53c3..274d8f6e9 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
@@ -46,7 +46,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
46 : $localize`Instance ${host} unmuted by your instance.` 46 : $localize`Instance ${host} unmuted by your instance.`
47 ) 47 )
48 48
49 this.loadData() 49 this.reloadData()
50 } 50 }
51 ) 51 )
52 } 52 }
@@ -69,13 +69,13 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
69 : $localize`Instance ${domain} muted by your instance.` 69 : $localize`Instance ${domain} muted by your instance.`
70 ) 70 )
71 71
72 this.loadData() 72 this.reloadData()
73 } 73 }
74 ) 74 )
75 }) 75 })
76 } 76 }
77 77
78 protected loadData () { 78 protected reloadData () {
79 const operation = this.mode === BlocklistComponentType.Account 79 const operation = this.mode === BlocklistComponentType.Account
80 ? this.blocklistService.getUserServerBlocklist({ 80 ? this.blocklistService.getUserServerBlocklist({
81 pagination: this.pagination, 81 pagination: this.pagination,
diff --git a/client/src/app/shared/shared-moderation/shared-moderation.module.ts b/client/src/app/shared/shared-moderation/shared-moderation.module.ts
index c7e201792..95213e2bd 100644
--- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts
+++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts
@@ -13,7 +13,7 @@ import { UserBanModalComponent } from './user-ban-modal.component'
13import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' 13import { UserModerationDropdownComponent } from './user-moderation-dropdown.component'
14import { VideoBlockComponent } from './video-block.component' 14import { VideoBlockComponent } from './video-block.component'
15import { VideoBlockService } from './video-block.service' 15import { VideoBlockService } from './video-block.service'
16import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' 16import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
17 17
18@NgModule({ 18@NgModule({
19 imports: [ 19 imports: [
@@ -21,7 +21,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou
21 SharedFormModule, 21 SharedFormModule,
22 SharedGlobalIconModule, 22 SharedGlobalIconModule,
23 SharedVideoCommentModule, 23 SharedVideoCommentModule,
24 SharedAccountAvatarModule 24 SharedActorImageModule
25 ], 25 ],
26 26
27 declarations: [ 27 declarations: [
diff --git a/client/src/app/shared/shared-moderation/video-block.component.scss b/client/src/app/shared/shared-moderation/video-block.component.scss
index afa0d96f7..a6e33070b 100644
--- a/client/src/app/shared/shared-moderation/video-block.component.scss
+++ b/client/src/app/shared/shared-moderation/video-block.component.scss
@@ -7,5 +7,5 @@ textarea {
7 7
8.live-info { 8.live-info {
9 font-size: 15px; 9 font-size: 15px;
10 margin: 40px 0 20px 0; 10 margin: 40px 0 20px;
11} 11}
diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts
index 30badc8fa..0e3924841 100644
--- a/client/src/app/shared/shared-search/advanced-search.model.ts
+++ b/client/src/app/shared/shared-search/advanced-search.model.ts
@@ -1,4 +1,4 @@
1import { NSFWQuery, SearchTargetType } from '@shared/models' 1import { BooleanBothQuery, SearchTargetType } from '@shared/models'
2 2
3export class AdvancedSearch { 3export class AdvancedSearch {
4 startDate: string // ISO 8601 4 startDate: string // ISO 8601
@@ -7,7 +7,7 @@ export class AdvancedSearch {
7 originallyPublishedStartDate: string // ISO 8601 7 originallyPublishedStartDate: string // ISO 8601
8 originallyPublishedEndDate: string // ISO 8601 8 originallyPublishedEndDate: string // ISO 8601
9 9
10 nsfw: NSFWQuery 10 nsfw: BooleanBothQuery
11 11
12 categoryOneOf: string 12 categoryOneOf: string
13 13
@@ -33,7 +33,7 @@ export class AdvancedSearch {
33 endDate?: string 33 endDate?: string
34 originallyPublishedStartDate?: string 34 originallyPublishedStartDate?: string
35 originallyPublishedEndDate?: string 35 originallyPublishedEndDate?: string
36 nsfw?: NSFWQuery 36 nsfw?: BooleanBothQuery
37 categoryOneOf?: string 37 categoryOneOf?: string
38 licenceOneOf?: string 38 licenceOneOf?: string
39 languageOneOf?: string 39 languageOneOf?: string
diff --git a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss
index ea59ab346..e678d6edf 100644
--- a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss
+++ b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss
@@ -11,7 +11,7 @@
11 width: 100%; 11 width: 100%;
12 position: absolute; 12 position: absolute;
13 bottom: 0; 13 bottom: 0;
14 background-color: rgba(0, 0, 0, 0.20); 14 background-color: rgba(0, 0, 0, 0.2);
15 15
16 div { 16 div {
17 height: 100%; 17 height: 100%;
@@ -39,8 +39,8 @@
39 top: 5px; 39 top: 5px;
40 font-weight: $font-bold; 40 font-weight: $font-bold;
41 41
42 &.warning { background-color: orange; } 42 &.warning { background-color: #ffa500; }
43 &.danger { background-color: red; } 43 &.danger { background-color: #ff0000; }
44} 44}
45 45
46.video-thumbnail-duration-overlay, 46.video-thumbnail-duration-overlay,
@@ -77,9 +77,9 @@
77 padding: 3px; 77 padding: 3px;
78 78
79 my-global-icon { 79 my-global-icon {
80 @include apply-svg-color(#fff);
81
80 width: 22px; 82 width: 22px;
81 height: 22px; 83 height: 22px;
82
83 @include apply-svg-color(#fff);
84 } 84 }
85} 85}
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.html b/client/src/app/shared/shared-user-settings/user-video-settings.component.html
index aa261fdce..a49e11485 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.html
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.html
@@ -5,7 +5,7 @@
5 <my-help> 5 <my-help>
6 <ng-template ptTemplate="customHtml"> 6 <ng-template ptTemplate="customHtml">
7 <ng-container i18n> 7 <ng-container i18n>
8 With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video. 8 With <strong>Hide</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
9 </ng-container> 9 </ng-container>
10 </ng-template> 10 </ng-template>
11 </my-help> 11 </my-help>
@@ -13,7 +13,7 @@
13 <div class="peertube-select-container"> 13 <div class="peertube-select-container">
14 <select id="nsfwPolicy" formControlName="nsfwPolicy" class="form-control"> 14 <select id="nsfwPolicy" formControlName="nsfwPolicy" class="form-control">
15 <option i18n value="undefined" disabled>Policy for sensitive videos</option> 15 <option i18n value="undefined" disabled>Policy for sensitive videos</option>
16 <option i18n value="do_not_list">Do not list</option> 16 <option i18n value="do_not_list">Hide</option>
17 <option i18n value="blur">Blur thumbnails</option> 17 <option i18n value="blur">Blur thumbnails</option>
18 <option i18n value="display">Display</option> 18 <option i18n value="display">Display</option>
19 </select> 19 </select>
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
index d74c2b2d8..ae95030c7 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
@@ -38,8 +38,6 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
38 ngOnInit () { 38 ngOnInit () {
39 this.allLanguagesGroup = $localize`All languages` 39 this.allLanguagesGroup = $localize`All languages`
40 40
41 let oldForm: any
42
43 this.buildForm({ 41 this.buildForm({
44 nsfwPolicy: null, 42 nsfwPolicy: null,
45 webTorrentEnabled: null, 43 webTorrentEnabled: null,
@@ -73,16 +71,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
73 videoLanguages 71 videoLanguages
74 }) 72 })
75 73
76 if (this.reactiveUpdate) { 74 if (this.reactiveUpdate) this.handleReactiveUpdate()
77 oldForm = { ...this.form.value }
78
79 this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => {
80 const updatedKey = Object.keys(formValue).find(k => formValue[k] !== oldForm[k])
81 oldForm = { ...this.form.value }
82
83 this.updateDetails([ updatedKey ])
84 })
85 }
86 }) 75 })
87 } 76 }
88 77
@@ -96,7 +85,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
96 const autoPlayVideo = this.form.value['autoPlayVideo'] 85 const autoPlayVideo = this.form.value['autoPlayVideo']
97 const autoPlayNextVideo = this.form.value['autoPlayNextVideo'] 86 const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
98 87
99 const videoLanguagesForm = this.form.value['videoLanguages'] 88 let videoLanguagesForm = this.form.value['videoLanguages']
100 89
101 if (Array.isArray(videoLanguagesForm)) { 90 if (Array.isArray(videoLanguagesForm)) {
102 if (videoLanguagesForm.length > 20) { 91 if (videoLanguagesForm.length > 20) {
@@ -104,13 +93,14 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
104 return 93 return
105 } 94 }
106 95
96 // Automatically use "All languages" if the user did not select any language
107 if (videoLanguagesForm.length === 0) { 97 if (videoLanguagesForm.length === 0) {
108 this.notifier.error($localize`You need to enable at least 1 video language.`) 98 videoLanguagesForm = [ this.allLanguagesGroup ]
109 return 99 this.form.patchValue({ videoLanguages: [ { group: this.allLanguagesGroup } ] })
110 } 100 }
111 } 101 }
112 102
113 const videoLanguages = this.getVideoLanguages(videoLanguagesForm) 103 const videoLanguages = this.buildLanguagesFromForm(videoLanguagesForm)
114 104
115 let details: UserUpdateMe = { 105 let details: UserUpdateMe = {
116 nsfwPolicy, 106 nsfwPolicy,
@@ -127,22 +117,13 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
127 if (onlyKeys) details = pick(details, onlyKeys) 117 if (onlyKeys) details = pick(details, onlyKeys)
128 118
129 if (this.authService.isLoggedIn()) { 119 if (this.authService.isLoggedIn()) {
130 this.userService.updateMyProfile(details).subscribe( 120 return this.updateLoggedProfile(details)
131 () => {
132 this.authService.refreshUserInformation()
133
134 if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`)
135 },
136
137 err => this.notifier.error(err.message)
138 )
139 } else {
140 this.userService.updateMyAnonymousProfile(details)
141 if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`)
142 } 121 }
122
123 return this.updateAnonymousProfile(details)
143 } 124 }
144 125
145 private getVideoLanguages (videoLanguages: ItemSelectCheckboxValue[]) { 126 private buildLanguagesFromForm (videoLanguages: ItemSelectCheckboxValue[]) {
146 if (!Array.isArray(videoLanguages)) return undefined 127 if (!Array.isArray(videoLanguages)) return undefined
147 128
148 // null means "All" 129 // null means "All"
@@ -166,4 +147,34 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
166 return l.id + '' 147 return l.id + ''
167 }) 148 })
168 } 149 }
150
151 private handleReactiveUpdate () {
152 let oldForm = { ...this.form.value }
153
154 this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => {
155 const updatedKey = Object.keys(formValue)
156 .find(k => formValue[k] !== oldForm[k])
157
158 oldForm = { ...this.form.value }
159
160 this.updateDetails([ updatedKey ])
161 })
162 }
163
164 private updateLoggedProfile (details: UserUpdateMe) {
165 this.userService.updateMyProfile(details).subscribe(
166 () => {
167 this.authService.refreshUserInformation()
168
169 if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`)
170 },
171
172 err => this.notifier.error(err.message)
173 )
174 }
175
176 private updateAnonymousProfile (details: UserUpdateMe) {
177 this.userService.updateMyAnonymousProfile(details)
178 if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`)
179 }
169} 180}
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss
index 698c5866a..73db0d090 100644
--- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss
+++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss
@@ -3,4 +3,4 @@
3.btn-remote-follow { 3.btn-remote-follow {
4 @include peertube-button; 4 @include peertube-button;
5 @include orange-button; 5 @include orange-button;
6} \ No newline at end of file 6}
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss b/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss
index f6cdc11c0..897ee7799 100644
--- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss
+++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss
@@ -8,8 +8,8 @@
8 float: right; 8 float: right;
9 padding: 0; 9 padding: 0;
10 10
11 & > .btn, 11 > .btn,
12 & > .dropdown > .dropdown-toggle { 12 > .dropdown > .dropdown-toggle {
13 font-size: 15px; 13 font-size: 15px;
14 } 14 }
15 15
@@ -20,7 +20,7 @@
20 &.big { 20 &.big {
21 height: 35px; 21 height: 35px;
22 22
23 & > button:first-child { 23 > button:first-child {
24 width: max-content; 24 width: max-content;
25 min-width: 175px; 25 min-width: 175px;
26 } 26 }
@@ -29,7 +29,7 @@
29 span:first-child { 29 span:first-child {
30 line-height: 80%; 30 line-height: 80%;
31 } 31 }
32 32
33 span:not(:first-child) { 33 span:not(:first-child) {
34 font-size: 75%; 34 font-size: 75%;
35 } 35 }
@@ -37,15 +37,15 @@
37 } 37 }
38 38
39 // Unlogged 39 // Unlogged
40 & > .dropdown > .dropdown-toggle span { 40 > .dropdown > .dropdown-toggle span {
41 padding-right: 3px; 41 padding-right: 3px;
42 } 42 }
43 43
44 // Logged 44 // Logged
45 & > .btn { 45 > .btn {
46 padding-right: 4px; 46 padding-right: 4px;
47 47
48 & + .dropdown > button { 48 + .dropdown > button {
49 padding-left: 2px; 49 padding-left: 2px;
50 50
51 &::after { 51 &::after {
diff --git a/client/src/app/shared/shared-video-comment/video-comment.service.ts b/client/src/app/shared/shared-video-comment/video-comment.service.ts
index 0f09778df..c5aeb3c12 100644
--- a/client/src/app/shared/shared-video-comment/video-comment.service.ts
+++ b/client/src/app/shared/shared-video-comment/video-comment.service.ts
@@ -190,13 +190,7 @@ export class VideoCommentService {
190 const filters = this.restService.parseQueryStringFilter(search, { 190 const filters = this.restService.parseQueryStringFilter(search, {
191 isLocal: { 191 isLocal: {
192 prefix: 'local:', 192 prefix: 'local:',
193 isBoolean: true, 193 isBoolean: true
194 handler: v => {
195 if (v === 'true') return v
196 if (v === 'false') return v
197
198 return undefined
199 }
200 }, 194 },
201 195
202 searchAccount: { prefix: 'account:' }, 196 searchAccount: { prefix: 'account:' },
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss
index 467ca1d2c..d9cf7a14f 100644
--- a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss
+++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss
@@ -3,7 +3,7 @@
3@import '_mixins'; 3@import '_mixins';
4@import '_miniature'; 4@import '_miniature';
5 5
6$iconSize: 16px; 6$icon-size: 16px;
7 7
8::ng-deep my-video-list-header { 8::ng-deep my-video-list-header {
9 display: flex; 9 display: flex;
@@ -17,20 +17,19 @@ $iconSize: 16px;
17 17
18 my-feed { 18 my-feed {
19 display: inline-block; 19 display: inline-block;
20 width: calc(#{$iconSize} - 2px); 20 width: calc(#{$icon-size} - 2px);
21 } 21 }
22 22
23 .moderation-block { 23 .moderation-block {
24
25 my-global-icon {
26 position: relative;
27 width: $iconSize;
28 }
29
30 margin-left: .4rem; 24 margin-left: .4rem;
31 display: flex; 25 display: flex;
32 justify-content: flex-end; 26 justify-content: flex-end;
33 align-items: center; 27 align-items: center;
28
29 my-global-icon {
30 position: relative;
31 width: $icon-size;
32 }
34 } 33 }
35} 34}
36 35
@@ -72,7 +71,7 @@ $iconSize: 16px;
72 71
73 .title-page { 72 .title-page {
74 margin-bottom: 10px; 73 margin-bottom: 10px;
75 margin-right: 0px; 74 margin-right: 0;
76 } 75 }
77 } 76 }
78} 77}
diff --git a/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts b/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
index 32cfdfd68..03be6d2ff 100644
--- a/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
+++ b/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
@@ -13,7 +13,7 @@ import { VideoDownloadComponent } from './video-download.component'
13import { VideoMiniatureComponent } from './video-miniature.component' 13import { VideoMiniatureComponent } from './video-miniature.component'
14import { VideosSelectionComponent } from './videos-selection.component' 14import { VideosSelectionComponent } from './videos-selection.component'
15import { VideoListHeaderComponent } from './video-list-header.component' 15import { VideoListHeaderComponent } from './video-list-header.component'
16import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' 16import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
17 17
18@NgModule({ 18@NgModule({
19 imports: [ 19 imports: [
@@ -25,7 +25,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou
25 SharedGlobalIconModule, 25 SharedGlobalIconModule,
26 SharedVideoLiveModule, 26 SharedVideoLiveModule,
27 SharedVideoModule, 27 SharedVideoModule,
28 SharedAccountAvatarModule 28 SharedActorImageModule
29 ], 29 ],
30 30
31 declarations: [ 31 declarations: [
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.scss b/client/src/app/shared/shared-video-miniature/video-download.component.scss
index 7f6e03c87..b689b1046 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.scss
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.scss
@@ -28,7 +28,7 @@
28 28
29 border-top-right-radius: 0; 29 border-top-right-radius: 0;
30 border-bottom-right-radius: 0; 30 border-bottom-right-radius: 0;
31 border-right: none; 31 border-right: 0;
32 32
33 select { 33 select {
34 height: inherit; 34 height: inherit;
@@ -85,7 +85,7 @@
85 &.metadata-attribute-tags { 85 &.metadata-attribute-tags {
86 .metadata-attribute-value:not(:nth-child(2)) { 86 .metadata-attribute-value:not(:nth-child(2)) {
87 &::before { 87 &::before {
88 content: ', ' 88 content: ', ';
89 } 89 }
90 } 90 }
91 } 91 }
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.html b/client/src/app/shared/shared-video-miniature/video-miniature.component.html
index bc19127aa..645be92bd 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.html
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.html
@@ -10,14 +10,15 @@
10 <div class="video-bottom"> 10 <div class="video-bottom">
11 <div class="video-miniature-information"> 11 <div class="video-miniature-information">
12 <div class="d-flex video-miniature-meta"> 12 <div class="d-flex video-miniature-meta">
13 <a *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" class="channel-avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> 13 <my-actor-avatar
14 <img [src]="getAvatarUrl()" alt="" /> 14 *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" [title]="channelLinkTitle"
15 </a> 15 [channel]="video.channel" [size]="actorImageSize" [internalHref]="[ '/video-channels', video.byVideoChannel ]"
16 ></my-actor-avatar>
16 17
17 <my-account-avatar 18 <my-actor-avatar
18 *ngIf="displayOptions.avatar && displayOwnerAccount()" [title]="channelLinkTitle" 19 *ngIf="displayOptions.avatar && displayOwnerAccount()" [title]="channelLinkTitle"
19 [account]="video.account" size="40" [internalHref]="'/video-channels/' + video.byVideoChannel" 20 [account]="video.account" [size]="actorImageSize" [internalHref]="[ '/video-channels', video.byVideoChannel ]"
20 ></my-account-avatar> 21 ></my-actor-avatar>
21 22
22 <div class="w-100 d-flex flex-column"> 23 <div class="w-100 d-flex flex-column">
23 <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name" 24 <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name"
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss
index f6f2925f0..5df89d019 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss
@@ -12,15 +12,10 @@ $more-button-width: 40px;
12 width: calc(100% - #{$more-button-width}); 12 width: calc(100% - #{$more-button-width});
13} 13}
14 14
15my-account-avatar, 15my-actor-avatar {
16.channel-avatar {
17 margin: 10px 10px 0 0; 16 margin: 10px 10px 0 0;
18} 17}
19 18
20.channel-avatar img{
21 @include channel-avatar(40px);
22}
23
24.video-miniature-created-at-views { 19.video-miniature-created-at-views {
25 font-size: 13px; 20 font-size: 13px;
26} 21}
@@ -46,7 +41,7 @@ my-account-avatar,
46} 41}
47 42
48.video-info-blocked { 43.video-info-blocked {
49 color: red; 44 color: #ff0000;
50 45
51 .blocked-reason::before { 46 .blocked-reason::before {
52 content: ' - '; 47 content: ' - ';
@@ -54,7 +49,7 @@ my-account-avatar,
54} 49}
55 50
56.video-info-nsfw { 51.video-info-nsfw {
57 color: red; 52 color: #ff0000;
58} 53}
59 54
60.video-actions { 55.video-actions {
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
index 8d66aaee2..b58c118be 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
@@ -12,6 +12,7 @@ import {
12} from '@angular/core' 12} from '@angular/core'
13import { AuthService, ScreenService, ServerService, User } from '@app/core' 13import { AuthService, ScreenService, ServerService, User } from '@app/core'
14import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' 14import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models'
15import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component'
15import { Video } from '../shared-main' 16import { Video } from '../shared-main'
16import { VideoPlaylistService } from '../shared-video-playlist' 17import { VideoPlaylistService } from '../shared-video-playlist'
17import { VideoActionsDisplayType } from './video-actions-dropdown.component' 18import { VideoActionsDisplayType } from './video-actions-dropdown.component'
@@ -51,6 +52,8 @@ export class VideoMiniatureComponent implements OnInit {
51 } 52 }
52 @Input() displayVideoActions = true 53 @Input() displayVideoActions = true
53 54
55 @Input() actorImageSize: ActorAvatarSize = '40'
56
54 @Input() displayAsRow = false 57 @Input() displayAsRow = false
55 58
56 @Input() videoLinkType: VideoLinkType = 'internal' 59 @Input() videoLinkType: VideoLinkType = 'internal'
@@ -180,14 +183,6 @@ export class VideoMiniatureComponent implements OnInit {
180 return '' 183 return ''
181 } 184 }
182 185
183 getAvatarUrl () {
184 if (this.displayOwnerAccount()) {
185 return this.video.account.avatar?.url
186 }
187
188 return this.video.videoChannelAvatarUrl
189 }
190
191 loadActions () { 186 loadActions () {
192 if (this.displayVideoActions) this.showActions = true 187 if (this.displayVideoActions) this.showActions = true
193 188
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html
index dec9e99f3..4ee90ce7f 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html
@@ -1,9 +1,9 @@
1<div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">No results.</div> 1<div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">{{ noResultMessage }}</div>
2 2
3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos"> 3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos">
4 <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> 4 <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById">
5 5
6 <div class="checkbox-container"> 6 <div class="checkbox-container" *ngIf="enableSelection">
7 <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox> 7 <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox>
8 </div> 8 </div>
9 9
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
index f8c3800d7..d64ee9b98 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
@@ -31,6 +31,9 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni
31 @Input() pagination: ComponentPagination 31 @Input() pagination: ComponentPagination
32 @Input() titlePage: string 32 @Input() titlePage: string
33 @Input() miniatureDisplayOptions: MiniatureDisplayOptions 33 @Input() miniatureDisplayOptions: MiniatureDisplayOptions
34 @Input() noResultMessage = $localize`No results.`
35 @Input() enableSelection = true
36 @Input() loadOnInit = true
34 37
35 @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> 38 @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>>
36 39
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss
index b84cacece..cb1168196 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss
@@ -126,7 +126,7 @@ $timestamp-margin-right: 10px;
126 border-top: 1px solid $separator-border-color; 126 border-top: 1px solid $separator-border-color;
127} 127}
128 128
129.new-playlist-button { 129.new-playlist-button {
130 cursor: pointer; 130 cursor: pointer;
131 131
132 my-global-icon { 132 my-global-icon {
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss
index 572f7d7a8..9ccd03912 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss
@@ -84,21 +84,23 @@ my-video-thumbnail,
84 width: auto; 84 width: auto;
85 } 85 }
86 86
87 .video-info-account, .video-info-timestamp { 87 .video-info-account,
88 .video-info-timestamp {
88 color: pvar(--greyForegroundColor); 89 color: pvar(--greyForegroundColor);
89 } 90 }
90 } 91 }
91 } 92 }
92 93
93 .video-info-name { 94 .video-info-name {
95 @include ellipsis;
96
94 font-size: 18px; 97 font-size: 18px;
95 font-weight: $font-semibold; 98 font-weight: $font-semibold;
96 display: inline-block; 99 display: inline-block;
97
98 @include ellipsis;
99 } 100 }
100 101
101 .more, my-edit-button { 102 .more,
103 my-edit-button {
102 justify-self: flex-end; 104 justify-self: flex-end;
103 margin-left: auto; 105 margin-left: auto;
104 cursor: pointer; 106 cursor: pointer;
@@ -118,7 +120,7 @@ my-video-thumbnail,
118 display: flex; 120 display: flex;
119 121
120 &::after { 122 &::after {
121 border: none; 123 border: 0;
122 } 124 }
123 } 125 }
124 } 126 }
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss
index 99089166c..a46a6e475 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss
@@ -6,7 +6,7 @@
6 display: inline-block; 6 display: inline-block;
7 width: 100%; 7 width: 100%;
8 8
9 &.no-videos:not(.to-manage){ 9 &.no-videos:not(.to-manage) {
10 a { 10 a {
11 cursor: default !important; 11 cursor: default !important;
12 } 12 }
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
index 9bec16d77..5b6ba9dbf 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
@@ -37,10 +37,8 @@ export class VideoPlaylist implements ServerVideoPlaylist {
37 embedUrl: string 37 embedUrl: string
38 38
39 ownerBy: string 39 ownerBy: string
40 ownerAvatarUrl: string
41 40
42 videoChannelBy?: string 41 videoChannelBy?: string
43 videoChannelAvatarUrl?: string
44 42
45 private thumbnailVersion: number 43 private thumbnailVersion: number
46 private originThumbnailUrl: string 44 private originThumbnailUrl: string
@@ -78,12 +76,10 @@ export class VideoPlaylist implements ServerVideoPlaylist {
78 76
79 this.ownerAccount = hash.ownerAccount 77 this.ownerAccount = hash.ownerAccount
80 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) 78 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host)
81 this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount)
82 79
83 if (hash.videoChannel) { 80 if (hash.videoChannel) {
84 this.videoChannel = hash.videoChannel 81 this.videoChannel = hash.videoChannel
85 this.videoChannelBy = Actor.CREATE_BY_STRING(hash.videoChannel.name, hash.videoChannel.host) 82 this.videoChannelBy = Actor.CREATE_BY_STRING(hash.videoChannel.name, hash.videoChannel.host)
86 this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.videoChannel)
87 } 83 }
88 84
89 this.privacy.label = peertubeTranslate(this.privacy.label, translations) 85 this.privacy.label = peertubeTranslate(this.privacy.label, translations)
diff --git a/client/src/assets/player/images/info.svg b/client/src/assets/player/images/info.svg
new file mode 100644
index 000000000..bd1d9c6ca
--- /dev/null
+++ b/client/src/assets/player/images/info.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-info"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg> \ No newline at end of file
diff --git a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
index e97925ab5..4275a5e5e 100644
--- a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
+++ b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
@@ -1,10 +1,10 @@
1import * as Hlsjs from 'hls.js/dist/hls.light.js'
2import { Events, Segment } from 'p2p-media-loader-core'
3import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
1import videojs from 'video.js' 4import videojs from 'video.js'
2import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings' 5import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings'
3import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
4import { Events, Segment } from 'p2p-media-loader-core'
5import { timeToInt } from '../utils' 6import { timeToInt } from '../utils'
6import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' 7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
7import * as Hlsjs from 'hls.js/dist/hls.light.js'
8 8
9registerConfigPlugin(videojs) 9registerConfigPlugin(videojs)
10registerSourceHandler(videojs) 10registerSourceHandler(videojs)
@@ -36,6 +36,9 @@ class P2pMediaLoaderPlugin extends Plugin {
36 36
37 private networkInfoInterval: any 37 private networkInfoInterval: any
38 38
39 private hlsjsCurrentLevel: number
40 private hlsjsLevels: Hlsjs.Level[]
41
39 constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions) { 42 constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions) {
40 super(player) 43 super(player)
41 44
@@ -84,6 +87,16 @@ class P2pMediaLoaderPlugin extends Plugin {
84 clearInterval(this.networkInfoInterval) 87 clearInterval(this.networkInfoInterval)
85 } 88 }
86 89
90 getCurrentLevel () {
91 return this.hlsjsLevels.find(l => l.level === this.hlsjsCurrentLevel)
92 }
93
94 getLiveLatency () {
95 return undefined as number
96 // FIXME: Use latency when hls >= V1
97 // return this.hlsjs.latency
98 }
99
87 getHLSJS () { 100 getHLSJS () {
88 return this.hlsjs 101 return this.hlsjs
89 } 102 }
@@ -140,6 +153,14 @@ class P2pMediaLoaderPlugin extends Plugin {
140 this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++) 153 this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++)
141 this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--) 154 this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--)
142 155
156 this.hlsjs.on(Hlsjs.Events.MANIFEST_PARSED, (_e, manifest) => {
157 this.hlsjsCurrentLevel = manifest.firstLevel
158 this.hlsjsLevels = manifest.levels
159 })
160 this.hlsjs.on(Hlsjs.Events.LEVEL_LOADED, (_e, level) => {
161 this.hlsjsCurrentLevel = level.levelId || (level as any).id
162 })
163
143 this.networkInfoInterval = setInterval(() => { 164 this.networkInfoInterval = setInterval(() => {
144 const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload) 165 const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload)
145 const p2pUploadSpeed = this.arraySum(this.statsP2PBytes.pendingUpload) 166 const p2pUploadSpeed = this.arraySum(this.statsP2PBytes.pendingUpload)
@@ -166,7 +187,8 @@ class P2pMediaLoaderPlugin extends Plugin {
166 numPeers: this.statsP2PBytes.numPeers, 187 numPeers: this.statsP2PBytes.numPeers,
167 downloaded: this.statsP2PBytes.totalDownload, 188 downloaded: this.statsP2PBytes.totalDownload,
168 uploaded: this.statsP2PBytes.totalUpload 189 uploaded: this.statsP2PBytes.totalUpload
169 } 190 },
191 bandwidthEstimate: (this.hlsjs as any).bandwidthEstimate / 8
170 } as PlayerNetworkInfo) 192 } as PlayerNetworkInfo)
171 }, this.CONSTANTS.INFO_SCHEDULER) 193 }, this.CONSTANTS.INFO_SCHEDULER)
172 } 194 }
diff --git a/client/src/assets/player/peertube-player-local-storage.ts b/client/src/assets/player/peertube-player-local-storage.ts
index cf2cfb472..80aceb239 100644
--- a/client/src/assets/player/peertube-player-local-storage.ts
+++ b/client/src/assets/player/peertube-player-local-storage.ts
@@ -45,6 +45,7 @@ function saveTheaterInStore (enabled: boolean) {
45} 45}
46 46
47function saveAverageBandwidth (value: number) { 47function saveAverageBandwidth (value: number) {
48 /** used to choose the most fitting resolution */
48 return setLocalStorage('average-bandwidth', value.toString()) 49 return setLocalStorage('average-bandwidth', value.toString())
49} 50}
50 51
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index ed82e0496..62dff8285 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -4,6 +4,8 @@ import 'videojs-contextmenu-pt'
4import 'videojs-contrib-quality-levels' 4import 'videojs-contrib-quality-levels'
5import './upnext/end-card' 5import './upnext/end-card'
6import './upnext/upnext-plugin' 6import './upnext/upnext-plugin'
7import './stats/stats-card'
8import './stats/stats-plugin'
7import './bezels/bezels-plugin' 9import './bezels/bezels-plugin'
8import './peertube-plugin' 10import './peertube-plugin'
9import './videojs-components/next-previous-video-button' 11import './videojs-components/next-previous-video-button'
@@ -170,6 +172,11 @@ export class PeertubePlayerManager {
170 self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) 172 self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle)
171 173
172 player.bezels() 174 player.bezels()
175 player.stats({
176 videoUUID: options.common.videoUUID,
177 videoIsLive: options.common.isLive,
178 mode
179 })
173 180
174 return res(player) 181 return res(player)
175 }) 182 })
@@ -538,6 +545,14 @@ export class PeertubePlayerManager {
538 }) 545 })
539 } 546 }
540 547
548 items.push({
549 icon: 'info',
550 label: player.localize('Stats for nerds'),
551 listener: () => {
552 player.stats().show()
553 }
554 })
555
541 return items.map(i => ({ 556 return items.map(i => ({
542 ...i, 557 ...i,
543 label: `<span class="vjs-icon-${i.icon || 'link-2'}"></span>` + i.label 558 label: `<span class="vjs-icon-${i.icon || 'link-2'}"></span>` + i.label
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts
index 4a6c80247..8afb424a7 100644
--- a/client/src/assets/player/peertube-videojs-typings.ts
+++ b/client/src/assets/player/peertube-videojs-typings.ts
@@ -7,7 +7,9 @@ import { PlayerMode } from './peertube-player-manager'
7import { PeerTubePlugin } from './peertube-plugin' 7import { PeerTubePlugin } from './peertube-plugin'
8import { PlaylistPlugin } from './playlist/playlist-plugin' 8import { PlaylistPlugin } from './playlist/playlist-plugin'
9import { EndCardOptions } from './upnext/end-card' 9import { EndCardOptions } from './upnext/end-card'
10import { StatsCardOptions } from './stats/stats-card'
10import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin' 11import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin'
12import { StatsForNerdsPlugin } from './stats/stats-plugin'
11 13
12declare module 'video.js' { 14declare module 'video.js' {
13 15
@@ -36,6 +38,8 @@ declare module 'video.js' {
36 38
37 bezels (): void 39 bezels (): void
38 40
41 stats (options?: StatsCardOptions): StatsForNerdsPlugin
42
39 qualityLevels (): QualityLevels 43 qualityLevels (): QualityLevels
40 44
41 textTracks (): TextTrackList & { 45 textTracks (): TextTrackList & {
@@ -195,6 +199,9 @@ type PlayerNetworkInfo = {
195 uploaded: number 199 uploaded: number
196 numPeers: number 200 numPeers: number
197 } 201 }
202
203 // In bytes
204 bandwidthEstimate: number
198} 205}
199 206
200type PlaylistItemOptions = { 207type PlaylistItemOptions = {
diff --git a/client/src/assets/player/stats/stats-card.ts b/client/src/assets/player/stats/stats-card.ts
new file mode 100644
index 000000000..d9f0d2fe9
--- /dev/null
+++ b/client/src/assets/player/stats/stats-card.ts
@@ -0,0 +1,273 @@
1import videojs from 'video.js'
2import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings'
3import { bytes, secondsToTime } from '../utils'
4
5interface StatsCardOptions extends videojs.ComponentOptions {
6 videoUUID: string
7 videoIsLive: boolean
8 mode: 'webtorrent' | 'p2p-media-loader'
9}
10
11interface PlayerNetworkInfo {
12 downloadSpeed?: string
13 uploadSpeed?: string
14 totalDownloaded?: string
15 totalUploaded?: string
16 numPeers?: number
17 averageBandwidth?: string
18
19 downloadedFromServer?: string
20 downloadedFromPeers?: string
21}
22
23const Component = videojs.getComponent('Component')
24class StatsCard extends Component {
25 options_: StatsCardOptions
26
27 container: HTMLDivElement
28
29 list: HTMLDivElement
30 closeButton: HTMLElement
31
32 updateInterval: any
33
34 mode: 'webtorrent' | 'p2p-media-loader'
35
36 metadataStore: any = {}
37
38 intervalMs = 300
39 playerNetworkInfo: PlayerNetworkInfo = {}
40
41 constructor (player: videojs.Player, options: StatsCardOptions) {
42 super(player, options)
43 }
44
45 createEl () {
46 const container = super.createEl('div', {
47 className: 'vjs-stats-content',
48 innerHTML: this.getMainTemplate()
49 }) as HTMLDivElement
50 this.container = container
51 this.container.style.display = 'none'
52
53 this.closeButton = this.container.getElementsByClassName('vjs-stats-close')[0] as HTMLElement
54 this.closeButton.onclick = () => this.hide()
55
56 this.list = this.container.getElementsByClassName('vjs-stats-list')[0] as HTMLDivElement
57
58 this.player_.on('p2pInfo', (event: any, data: EventPlayerNetworkInfo) => {
59 if (!data) return // HTTP fallback
60
61 this.mode = data.source
62
63 const p2pStats = data.p2p
64 const httpStats = data.http
65
66 this.playerNetworkInfo.downloadSpeed = bytes(p2pStats.downloadSpeed + httpStats.downloadSpeed).join(' ')
67 this.playerNetworkInfo.uploadSpeed = bytes(p2pStats.uploadSpeed + httpStats.uploadSpeed).join(' ')
68 this.playerNetworkInfo.totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded).join(' ')
69 this.playerNetworkInfo.totalUploaded = bytes(p2pStats.uploaded + httpStats.uploaded).join(' ')
70 this.playerNetworkInfo.numPeers = p2pStats.numPeers
71 this.playerNetworkInfo.averageBandwidth = bytes(data.bandwidthEstimate).join(' ') + '/s'
72
73 if (data.source === 'p2p-media-loader') {
74 this.playerNetworkInfo.downloadedFromServer = bytes(httpStats.downloaded).join(' ')
75 this.playerNetworkInfo.downloadedFromPeers = bytes(p2pStats.downloaded).join(' ')
76 }
77 })
78
79 return container
80 }
81
82 toggle () {
83 this.updateInterval
84 ? this.hide()
85 : this.show()
86 }
87
88 show () {
89 this.container.style.display = 'block'
90 this.updateInterval = setInterval(async () => {
91 try {
92 const options = this.mode === 'webtorrent'
93 ? await this.buildWebTorrentOptions()
94 : await this.buildHLSOptions()
95
96 this.list.innerHTML = this.getListTemplate(options)
97 } catch (err) {
98 console.error('Cannot update stats.', err)
99 clearInterval(this.updateInterval)
100 }
101 }, this.intervalMs)
102 }
103
104 hide () {
105 clearInterval(this.updateInterval)
106 this.container.style.display = 'none'
107 }
108
109 private async buildHLSOptions () {
110 const p2pMediaLoader = this.player_.p2pMediaLoader()
111 const level = p2pMediaLoader.getCurrentLevel()
112
113 const codecs = level?.videoCodec || level?.audioCodec
114 ? `${level?.videoCodec || ''} / ${level?.audioCodec || ''}`
115 : undefined
116
117 const resolution = `${level?.height}p${level?.attrs['FRAME-RATE'] || ''}`
118 const buffer = this.timeRangesToString(this.player().buffered())
119
120 let progress: number
121 let latency: string
122
123 if (this.options_.videoIsLive) {
124 latency = secondsToTime(p2pMediaLoader.getLiveLatency())
125 } else {
126 progress = this.player().bufferedPercent()
127 }
128
129 return {
130 playerNetworkInfo: this.playerNetworkInfo,
131 resolution,
132 codecs,
133 buffer,
134 latency,
135 progress
136 }
137 }
138
139 private async buildWebTorrentOptions () {
140 const videoFile = this.player_.webtorrent().getCurrentVideoFile()
141
142 if (!this.metadataStore[videoFile.fileUrl]) {
143 this.metadataStore[videoFile.fileUrl] = await fetch(videoFile.metadataUrl).then(res => res.json())
144 }
145
146 const metadata = this.metadataStore[videoFile.fileUrl]
147
148 let colorSpace = 'unknown'
149 let codecs = 'unknown'
150
151 if (metadata?.streams[0]) {
152 const stream = metadata.streams[0]
153
154 colorSpace = stream['color_space'] !== 'unknown'
155 ? stream['color_space']
156 : 'bt709'
157
158 codecs = stream['codec_name'] || 'avc1'
159 }
160
161 const resolution = videoFile?.resolution.label + videoFile?.fps
162 const buffer = this.timeRangesToString(this.player().buffered())
163 const progress = this.player_.webtorrent().getTorrent()?.progress
164
165 return {
166 playerNetworkInfo: this.playerNetworkInfo,
167 progress,
168 colorSpace,
169 codecs,
170 resolution,
171 buffer
172 }
173 }
174
175 private getListTemplate (options: {
176 playerNetworkInfo: PlayerNetworkInfo
177 progress: number
178 codecs: string
179 resolution: string
180 buffer: string
181
182 latency?: string
183 colorSpace?: string
184 }) {
185 const { playerNetworkInfo, progress, colorSpace, codecs, resolution, buffer, latency } = options
186 const player = this.player()
187
188 const videoQuality: VideoPlaybackQuality = player.getVideoPlaybackQuality()
189 const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
190 const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
191 const pr = (window.devicePixelRatio || 1).toFixed(2)
192 const frames = `${vw}x${vh}*${pr} / ${videoQuality.droppedVideoFrames} dropped of ${videoQuality.totalVideoFrames}`
193
194 const duration = player.duration()
195
196 let volume = `${Math.round(player.volume() * 100)}`
197 if (player.muted()) volume += ' (muted)'
198
199 const networkActivity = playerNetworkInfo.downloadSpeed
200 ? `${playerNetworkInfo.downloadSpeed} &dArr; / ${playerNetworkInfo.uploadSpeed} &uArr;`
201 : undefined
202
203 const totalTransferred = playerNetworkInfo.totalDownloaded
204 ? `${playerNetworkInfo.totalDownloaded} &dArr; / ${playerNetworkInfo.totalUploaded} &uArr;`
205 : undefined
206 const downloadBreakdown = playerNetworkInfo.downloadedFromServer
207 ? `${playerNetworkInfo.downloadedFromServer} from server · ${playerNetworkInfo.downloadedFromPeers} from peers`
208 : undefined
209
210 const bufferProgress = progress !== undefined
211 ? `${(progress * 100).toFixed(1)}% (${(progress * duration).toFixed(1)}s)`
212 : undefined
213
214 return `
215 ${this.buildElement(player.localize('Player mode'), this.options_.mode)}
216
217 ${this.buildElement(player.localize('Video UUID'), this.options_.videoUUID)}
218
219 ${this.buildElement(player.localize('Viewport / Frames'), frames)}
220
221 ${this.buildElement(player.localize('Resolution'), resolution)}
222
223 ${this.buildElement(player.localize('Volume'), volume)}
224
225 ${this.buildElement(player.localize('Codecs'), codecs)}
226 ${this.buildElement(player.localize('Color'), colorSpace)}
227
228 ${this.buildElement(player.localize('Connection Speed'), playerNetworkInfo.averageBandwidth)}
229
230 ${this.buildElement(player.localize('Network Activity'), networkActivity)}
231 ${this.buildElement(player.localize('Total Transfered'), totalTransferred)}
232 ${this.buildElement(player.localize('Download Breakdown'), downloadBreakdown)}
233
234 ${this.buildElement(player.localize('Buffer Progress'), bufferProgress)}
235 ${this.buildElement(player.localize('Buffer State'), buffer)}
236
237 ${this.buildElement(player.localize('Live Latency'), latency)}
238 `
239 }
240
241 private getMainTemplate () {
242 return `
243 <button class="vjs-stats-close" tabindex=0 aria-label="Close stats" title="Close stats">[x]</button>
244 <div class="vjs-stats-list"></div>
245 `
246 }
247
248 private buildElement (label: string, value?: string) {
249 if (!value) return ''
250
251 return `<div><div>${label}</div><span>${value}</span></div>`
252 }
253
254 private timeRangesToString (r: videojs.TimeRange) {
255 let result = ''
256
257 for (let i = 0; i < r.length; i++) {
258 const start = Math.floor(r.start(i))
259 const end = Math.floor(r.end(i))
260
261 result += `[${secondsToTime(start)}, ${secondsToTime(end)}] `
262 }
263
264 return result
265 }
266}
267
268videojs.registerComponent('StatsCard', StatsCard)
269
270export {
271 StatsCard,
272 StatsCardOptions
273}
diff --git a/client/src/assets/player/stats/stats-plugin.ts b/client/src/assets/player/stats/stats-plugin.ts
new file mode 100644
index 000000000..8aad80e8a
--- /dev/null
+++ b/client/src/assets/player/stats/stats-plugin.ts
@@ -0,0 +1,31 @@
1import videojs from 'video.js'
2import { StatsCard, StatsCardOptions } from './stats-card'
3
4const Plugin = videojs.getPlugin('plugin')
5
6class StatsForNerdsPlugin extends Plugin {
7 private statsCard: StatsCard
8
9 constructor (player: videojs.Player, options: StatsCardOptions) {
10 const settings = {
11 ...options
12 }
13
14 super(player)
15
16 this.player.ready(() => {
17 player.addClass('vjs-stats-for-nerds')
18 })
19
20 this.statsCard = new StatsCard(player, options)
21
22 player.addChild(this.statsCard, settings)
23 }
24
25 show () {
26 this.statsCard.show()
27 }
28}
29
30videojs.registerPlugin('stats', StatsForNerdsPlugin)
31export { StatsForNerdsPlugin }
diff --git a/client/src/assets/player/videojs-components/settings-menu-button.ts b/client/src/assets/player/videojs-components/settings-menu-button.ts
index e67a3da06..74788a897 100644
--- a/client/src/assets/player/videojs-components/settings-menu-button.ts
+++ b/client/src/assets/player/videojs-components/settings-menu-button.ts
@@ -95,11 +95,6 @@ class SettingsButton extends Button {
95 } 95 }
96 } 96 }
97 97
98 document.removeEventListener('click', this.documentClickHandler)
99 if (this.isInIframe()) {
100 window.removeEventListener('blur', this.documentClickHandler)
101 }
102
103 this.hideDialog() 98 this.hideDialog()
104 99
105 if (this.settingsButtonOptions.entries.length === 0) { 100 if (this.settingsButtonOptions.entries.length === 0) {
@@ -107,6 +102,14 @@ class SettingsButton extends Button {
107 } 102 }
108 } 103 }
109 104
105 dispose () {
106 document.removeEventListener('click', this.documentClickHandler)
107
108 if (this.isInIframe()) {
109 window.removeEventListener('blur', this.documentClickHandler)
110 }
111 }
112
110 onAddSettingsItem (event: any, data: any) { 113 onAddSettingsItem (event: any, data: any) {
111 const [ entry, options ] = data 114 const [ entry, options ] = data
112 115
diff --git a/client/src/assets/player/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
index e557fe722..6f5fbe4c9 100644
--- a/client/src/assets/player/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
@@ -506,7 +506,8 @@ class WebTorrentPlugin extends Plugin {
506 uploadSpeed: this.torrent.uploadSpeed, 506 uploadSpeed: this.torrent.uploadSpeed,
507 downloaded: this.torrent.downloaded, 507 downloaded: this.torrent.downloaded,
508 uploaded: this.torrent.uploaded 508 uploaded: this.torrent.uploaded
509 } 509 },
510 bandwidthEstimate: this.webtorrent.downloadSpeed
510 } as PlayerNetworkInfo) 511 } as PlayerNetworkInfo)
511 }, this.CONSTANTS.INFO_SCHEDULER) 512 }, this.CONSTANTS.INFO_SCHEDULER)
512 } 513 }
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index fa9c0d992..89b6f0c4c 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -8,9 +8,9 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
8 8
9@import './bootstrap'; 9@import './bootstrap';
10@import './primeng-custom'; 10@import './primeng-custom';
11@import './ng-select.scss'; 11@import './ng-select';
12 12
13@import './classes.scss'; 13@import './classes';
14 14
15[hidden] { 15[hidden] {
16 display: none !important; 16 display: none !important;
@@ -89,14 +89,16 @@ input.readonly {
89 background-color: pvar(--inputBackgroundColor) !important; 89 background-color: pvar(--inputBackgroundColor) !important;
90} 90}
91 91
92input, textarea { 92input,
93textarea {
93 outline: none; 94 outline: none;
94 color: pvar(--inputForegroundColor); 95 color: pvar(--inputForegroundColor);
95} 96}
96 97
97button { 98button {
98 background: unset;
99 @include disable-outline; 99 @include disable-outline;
100
101 background: unset;
100} 102}
101 103
102label { 104label {
@@ -121,12 +123,12 @@ code {
121 margin-top: 5px; 123 margin-top: 5px;
122} 124}
123 125
124.input-error 126.input-error,
125my-input-toggle-hidden ::ng-deep input { 127my-input-toggle-hidden ::ng-deep input {
126 border-color: $red !important; 128 border-color: $red !important;
127} 129}
128 130
129.fullWidth { 131.full-width {
130 width: 100%; 132 width: 100%;
131 margin-left: auto; 133 margin-left: auto;
132 margin-right: auto; 134 margin-right: auto;
@@ -134,7 +136,7 @@ my-input-toggle-hidden ::ng-deep input {
134} 136}
135 137
136.glyphicon-black { 138.glyphicon-black {
137 color: black; 139 color: #000;
138} 140}
139 141
140.row { 142.row {
@@ -184,26 +186,26 @@ my-input-toggle-hidden ::ng-deep input {
184 width: 100%; 186 width: 100%;
185 } 187 }
186 188
187 &.lock-scroll .main-row > router-outlet + * { 189 &.lock-scroll .main-row > router-outlet + * { /* stylelint-disable-line selector-max-compound-selectors */
188 // Lock and hide body scrollbars 190 // Lock and hide body scrollbars
189 position: fixed; 191 position: fixed;
190 192
191 // Lock and hide sub-menu scrollbars 193 // Lock and hide sub-menu scrollbars
192 .sub-menu { 194 .sub-menu { /* stylelint-disable-line */
193 overflow-x: hidden; 195 overflow-x: hidden;
194 } 196 }
195 } 197 }
196} 198}
197 199
198.title-page { 200.title-page {
201 @include disable-default-a-behaviour;
202
199 opacity: 0.6; 203 opacity: 0.6;
200 color: pvar(--mainForegroundColor); 204 color: pvar(--mainForegroundColor);
201 font-size: 16px; 205 font-size: 16px;
202 display: inline-block; 206 display: inline-block;
203 margin-right: 55px; 207 margin-right: 55px;
204 font-weight: $font-semibold; 208 font-weight: $font-semibold;
205 @include disable-default-a-behaviour;
206
207 border-bottom: 2px solid transparent; 209 border-bottom: 2px solid transparent;
208 210
209 &.title-page-single { 211 &.title-page-single {
@@ -219,13 +221,19 @@ my-input-toggle-hidden ::ng-deep input {
219 font-size: 125%; 221 font-size: 125%;
220 } 222 }
221 223
222 &:hover, &:active, &:focus { 224 &:hover,
225 &:active,
226 &:focus {
223 color: pvar(--mainForegroundColor); 227 color: pvar(--mainForegroundColor);
224 } 228 }
225 229
226 &.active, &:hover, &:active, &:focus, &.title-page-single { 230 &.active,
231 &:hover,
232 &:active,
233 &:focus,
234 &.title-page-single {
227 opacity: 1; 235 opacity: 1;
228 outline: 0px hidden !important; 236 outline: 0 hidden !important;
229 } 237 }
230 238
231 @media screen and (max-width: $mobile-view) { 239 @media screen and (max-width: $mobile-view) {
@@ -262,7 +270,10 @@ my-input-toggle-hidden ::ng-deep input {
262 background-color: pvar(--submenuBackgroundColor); 270 background-color: pvar(--submenuBackgroundColor);
263 } 271 }
264 272
265 &.active, &:hover, &:active, &:focus { 273 &.active,
274 &:hover,
275 &:active,
276 &:focus {
266 opacity: 1; 277 opacity: 1;
267 } 278 }
268 } 279 }
@@ -275,8 +286,13 @@ my-input-toggle-hidden ::ng-deep input {
275 286
276// In tables, don't have a hover different background 287// In tables, don't have a hover different background
277table { 288table {
278 .action-button-edit, .action-button-delete { 289 .action-button-edit,
279 &:hover, &:active, &:focus, &[disabled], &.disabled { 290 .action-button-delete {
291 &:hover,
292 &:active,
293 &:focus,
294 &[disabled],
295 &.disabled {
280 background-color: $grey-background-color !important; 296 background-color: $grey-background-color !important;
281 } 297 }
282 } 298 }
@@ -329,15 +345,12 @@ ngx-loading-bar {
329 345
330@media screen and (max-width: #{breakpoint(xxl)}) { 346@media screen and (max-width: #{breakpoint(xxl)}) {
331 .main-col { 347 .main-col {
332 & { 348 --horizontalMarginContent: #{$not-expanded-horizontal-margins / 2};
333 --horizontalMarginContent: #{$not-expanded-horizontal-margins / 2}; 349 --videosHorizontalMarginContent: 30px;
334 }
335 350
336 &.expanded { 351 &.expanded {
337 --horizontalMarginContent: #{$expanded-horizontal-margins / 2}; 352 --horizontalMarginContent: #{$expanded-horizontal-margins / 2};
338 } 353 }
339
340 --videosHorizontalMarginContent: 30px;
341 } 354 }
342} 355}
343 356
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss
index 0ab6230c8..548e55e1e 100644
--- a/client/src/sass/bootstrap.scss
+++ b/client/src/sass/bootstrap.scss
@@ -6,7 +6,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
6 6
7// Thanks https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d 7// Thanks https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d
8.glyphicon-refresh-animate { 8.glyphicon-refresh-animate {
9 animation: spin .7s infinite linear; 9 animation: spin 0.7s infinite linear;
10} 10}
11 11
12.glyphicon-duplicate { 12.glyphicon-duplicate {
@@ -25,6 +25,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
25 from { 25 from {
26 transform: scale(1) rotate(0deg); 26 transform: scale(1) rotate(0deg);
27 } 27 }
28
28 to { 29 to {
29 transform: scale(1) rotate(360deg); 30 transform: scale(1) rotate(360deg);
30 } 31 }
@@ -70,7 +71,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
70 &.active { 71 &.active {
71 color: pvar(--mainBackgroundColor) !important; 72 color: pvar(--mainBackgroundColor) !important;
72 background-color: pvar(--mainHoverColor); 73 background-color: pvar(--mainHoverColor);
73 opacity: .9; 74 opacity: 0.9;
74 } 75 }
75 76
76 &:active { 77 &:active {
@@ -97,9 +98,9 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
97} 98}
98 99
99@media screen and (min-width: #{breakpoint(md)}) { 100@media screen and (min-width: #{breakpoint(md)}) {
100 .modal:before { 101 .modal::before {
101 vertical-align: middle; 102 vertical-align: middle;
102 content: " "; 103 content: ' ';
103 height: 100%; 104 height: 100%;
104 } 105 }
105 106
@@ -123,7 +124,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
123 } 124 }
124 125
125 .modal-header { 126 .modal-header {
126 border-bottom: none; 127 border-bottom: 0;
127 margin-bottom: 5px; 128 margin-bottom: 5px;
128 129
129 .modal-title { 130 .modal-title {
@@ -140,10 +141,11 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
140 141
141 margin: 0; 142 margin: 0;
142 padding: 0; 143 padding: 0;
143 opacity: .5; 144 opacity: 0.5;
144 145
145 &[iconName="cross"] { 146 &[iconName=cross] { /* stylelint-disable-line selector-max-compound-selectors */
146 @include icon(16px); 147 @include icon(16px);
148
147 top: -3px; 149 top: -3px;
148 } 150 }
149 } 151 }
@@ -154,7 +156,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
154 text-align: right; 156 text-align: right;
155 157
156 > .peertube-button:not(:first-child) { 158 > .peertube-button:not(:first-child) {
157 margin-left: 10px 159 margin-left: 10px;
158 } 160 }
159 } 161 }
160} 162}
@@ -168,7 +170,8 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
168 170
169// On touchscreen devices, simply overflow: hidden to avoid detached overlay on scroll 171// On touchscreen devices, simply overflow: hidden to avoid detached overlay on scroll
170@media (hover: none) and (pointer: coarse) { 172@media (hover: none) and (pointer: coarse) {
171 .modal-open, .menu-open { 173 .modal-open,
174 .menu-open {
172 overflow: hidden !important; 175 overflow: hidden !important;
173 } 176 }
174 177
@@ -176,7 +179,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
176 .menu-open { 179 .menu-open {
177 .main-col { 180 .main-col {
178 &::before { 181 &::before {
179 background-color: black; 182 background-color: #000;
180 width: 100vw; 183 width: 100vw;
181 height: 100vh; 184 height: 100vh;
182 opacity: 0.75; 185 opacity: 0.75;
@@ -204,7 +207,10 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
204 .nav-link { 207 .nav-link {
205 opacity: 0.6 !important; 208 opacity: 0.6 !important;
206 209
207 &.active, &:hover, &:active, &:focus { 210 &.active,
211 &:hover,
212 &:active,
213 &:focus {
208 opacity: 1 !important; 214 opacity: 1 !important;
209 } 215 }
210 } 216 }
@@ -221,7 +227,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
221 227
222 color: pvar(--mainForegroundColor); 228 color: pvar(--mainForegroundColor);
223 font-weight: $font-semibold; 229 font-weight: $font-semibold;
224 border: none; 230 border: 0;
225 border-bottom: 2px solid transparent; 231 border-bottom: 2px solid transparent;
226 opacity: 0.6; 232 opacity: 0.6;
227 233
@@ -231,7 +237,10 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
231 border-bottom-color: pvar(--mainColor); 237 border-bottom-color: pvar(--mainColor);
232 } 238 }
233 239
234 &.active, &:hover, &:active, &:focus { 240 &.active,
241 &:hover,
242 &:active,
243 &:focus {
235 opacity: 1; 244 opacity: 1;
236 } 245 }
237} 246}
@@ -314,9 +323,10 @@ ngb-tooltip-window {
314} 323}
315 324
316.input-group { 325.input-group {
317 & > .form-control { 326 > .form-control {
318 flex: initial; 327 flex: initial;
319 } 328 }
329
320 input.form-control { 330 input.form-control {
321 width: unset !important; 331 width: unset !important;
322 flex-grow: 1; 332 flex-grow: 1;
@@ -366,7 +376,7 @@ ngb-tooltip-window {
366 border: 1px solid #eee; 376 border: 1px solid #eee;
367 border-radius: .25rem; 377 border-radius: .25rem;
368 378
369 & > label { 379 > label {
370 position: relative; 380 position: relative;
371 top: -5px; 381 top: -5px;
372 left: -10px; 382 left: -10px;
diff --git a/client/src/sass/include/_actor.scss b/client/src/sass/include/_actor.scss
index a4798ce1d..38bd90ae6 100644
--- a/client/src/sass/include/_actor.scss
+++ b/client/src/sass/include/_actor.scss
@@ -17,7 +17,7 @@
17@mixin show-more-description { 17@mixin show-more-description {
18 color: pvar(--mainColor); 18 color: pvar(--mainColor);
19 cursor: pointer; 19 cursor: pointer;
20 margin: 10px auto 45px auto; 20 margin: 10px auto 45px;
21} 21}
22 22
23@mixin avatar-row-responsive ($img-margin, $grey-font-size) { 23@mixin avatar-row-responsive ($img-margin, $grey-font-size) {
@@ -25,8 +25,8 @@
25 grid-column: 1; 25 grid-column: 1;
26 margin-bottom: 30px; 26 margin-bottom: 30px;
27 27
28 .channel-avatar { 28 .main-avatar {
29 @include channel-avatar(120px); 29 @include actor-avatar-size(120px);
30 } 30 }
31 31
32 > div { 32 > div {
@@ -77,12 +77,8 @@
77 font-size: 22px; 77 font-size: 22px;
78 } 78 }
79 79
80 .channel-avatar { 80 .main-avatar {
81 @include channel-avatar(80px); 81 @include actor-avatar-size(80px);
82 }
83
84 .account-avatar {
85 @include avatar(120px);
86 } 82 }
87 } 83 }
88} 84}
diff --git a/client/src/sass/include/_bootstrap.scss b/client/src/sass/include/_bootstrap.scss
index b1a23be6b..d9e5efc02 100644
--- a/client/src/sass/include/_bootstrap.scss
+++ b/client/src/sass/include/_bootstrap.scss
@@ -1,4 +1,4 @@
1@import "./_bootstrap-variables"; 1@import './_bootstrap-variables';
2 2
3@import '~bootstrap/scss/functions'; 3@import '~bootstrap/scss/functions';
4@import '~bootstrap/scss/variables'; 4@import '~bootstrap/scss/variables';
diff --git a/client/src/sass/include/_fonts.scss b/client/src/sass/include/_fonts.scss
index 6313736e0..514261d01 100644
--- a/client/src/sass/include/_fonts.scss
+++ b/client/src/sass/include/_fonts.scss
@@ -1,4 +1,4 @@
1@font-face{ 1@font-face {
2 font-family: 'Source Sans Pro'; 2 font-family: 'Source Sans Pro';
3 font-weight: 200 900; 3 font-weight: 200 900;
4 font-style: normal; 4 font-style: normal;
@@ -7,7 +7,7 @@
7 src: url('../fonts/source-sans/WOFF2/VAR/SourceSans3VF-Roman.ttf.woff2') format('woff2'); 7 src: url('../fonts/source-sans/WOFF2/VAR/SourceSans3VF-Roman.ttf.woff2') format('woff2');
8} 8}
9 9
10@font-face{ 10@font-face {
11 font-family: 'Source Sans Pro'; 11 font-family: 'Source Sans Pro';
12 font-weight: 200 900; 12 font-weight: 200 900;
13 font-style: italic; 13 font-style: italic;
diff --git a/client/src/sass/include/_miniature.scss b/client/src/sass/include/_miniature.scss
index 3b86f29b4..070aa3398 100644
--- a/client/src/sass/include/_miniature.scss
+++ b/client/src/sass/include/_miniature.scss
@@ -3,9 +3,8 @@
3 3
4@mixin miniature-name { 4@mixin miniature-name {
5 @include ellipsis-multiline(1.1em, 2); 5 @include ellipsis-multiline(1.1em, 2);
6 @include peertube-word-wrap(false);
6 7
7 word-break: break-all;
8 word-wrap: break-word;
9 transition: color 0.2s; 8 transition: color 0.2s;
10 font-weight: $font-semibold; 9 font-weight: $font-semibold;
11 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
@@ -21,12 +20,12 @@
21} 20}
22 21
23@mixin miniature-thumbnail { 22@mixin miniature-thumbnail {
24 @include disable-outline;
25
26 $play-overlay-transition: 0.2s ease; 23 $play-overlay-transition: 0.2s ease;
27 $play-overlay-height: 26px; 24 $play-overlay-height: 26px;
28 $play-overlay-width: 18px; 25 $play-overlay-width: 18px;
29 26
27 @include disable-outline;
28
30 display: flex; 29 display: flex;
31 flex-direction: column; 30 flex-direction: column;
32 position: relative; 31 position: relative;
@@ -47,7 +46,8 @@
47 opacity: 0; 46 opacity: 0;
48 background-color: rgba(0, 0, 0, 0.3); 47 background-color: rgba(0, 0, 0, 0.3);
49 48
50 &, .icon { 49 &,
50 .icon {
51 transition: all $play-overlay-transition; 51 transition: all $play-overlay-transition;
52 } 52 }
53 53
@@ -79,7 +79,7 @@
79 79
80 &.blur-filter { 80 &.blur-filter {
81 filter: blur(20px); 81 filter: blur(20px);
82 transform : scale(1.03); 82 transform: scale(1.03);
83 } 83 }
84 } 84 }
85} 85}
@@ -129,10 +129,7 @@
129 column-gap: 30px; 129 column-gap: 30px;
130 grid-template-columns: repeat( 130 grid-template-columns: repeat(
131 auto-fill, 131 auto-fill,
132 minmax( 132 minmax(var(--miniatureMinWidth), 1fr)
133 var(--miniatureMinWidth),
134 1fr
135 )
136 ); 133 );
137 134
138 .video-wrapper, 135 .video-wrapper,
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index e03201cef..b2083e516 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -1,7 +1,9 @@
1@import '_variables'; 1@import '_variables';
2 2
3@mixin disable-default-a-behaviour { 3@mixin disable-default-a-behaviour {
4 &:hover, &:focus, &:active { 4 &:hover,
5 &:focus,
6 &:active {
5 text-decoration: none !important; 7 text-decoration: none !important;
6 outline: none !important; 8 outline: none !important;
7 } 9 }
@@ -22,7 +24,7 @@
22@mixin ellipsis-multiline($font-size: 16px, $number-of-lines: 2) { 24@mixin ellipsis-multiline($font-size: 16px, $number-of-lines: 2) {
23 display: block; 25 display: block;
24 /* Fallback for non-webkit */ 26 /* Fallback for non-webkit */
25 display: -webkit-box; 27 display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */
26 -webkit-line-clamp: $number-of-lines; 28 -webkit-line-clamp: $number-of-lines;
27 /* Fallback for non-webkit */ 29 /* Fallback for non-webkit */
28 font-size: $font-size; 30 font-size: $font-size;
@@ -36,7 +38,7 @@
36 position: relative; 38 position: relative;
37 overflow: hidden; 39 overflow: hidden;
38 40
39 &:after { 41 &::after {
40 content: ''; 42 content: '';
41 pointer-events: none; 43 pointer-events: none;
42 width: 100%; 44 width: 100%;
@@ -48,11 +50,14 @@
48 } 50 }
49} 51}
50 52
51@mixin peertube-word-wrap { 53@mixin peertube-word-wrap ($with-hyphen: true) {
52 word-break: break-word; 54 word-break: break-word;
53 word-wrap: break-word; 55 word-wrap: break-word;
54 overflow-wrap: break-word; 56 overflow-wrap: break-word;
55 hyphens: auto; 57
58 @if $with-hyphen {
59 hyphens: auto;
60 }
56} 61}
57 62
58@mixin apply-svg-color ($color) { 63@mixin apply-svg-color ($color) {
@@ -109,9 +114,9 @@
109 padding-bottom: 0; 114 padding-bottom: 0;
110 flex-wrap: nowrap; 115 flex-wrap: nowrap;
111 116
112 .input-group-text{ 117 .input-group-text {
113 font-size: 14px; 118 font-size: 14px;
114 color: gray; 119 color: #808080;
115 } 120 }
116} 121}
117 122
@@ -128,7 +133,9 @@
128@mixin orange-button { 133@mixin orange-button {
129 @include button-focus(pvar(--mainColorLightest)); 134 @include button-focus(pvar(--mainColorLightest));
130 135
131 &, &:active, &:focus { 136 &,
137 &:active,
138 &:focus {
132 color: #fff; 139 color: #fff;
133 background-color: pvar(--mainColor); 140 background-color: pvar(--mainColor);
134 } 141 }
@@ -138,14 +145,15 @@
138 background-color: pvar(--mainHoverColor); 145 background-color: pvar(--mainHoverColor);
139 } 146 }
140 147
141 &[disabled], &.disabled { 148 &[disabled],
149 &.disabled {
142 cursor: default; 150 cursor: default;
143 color: #fff; 151 color: #fff;
144 background-color: #C6C6C6; 152 background-color: #C6C6C6;
145 } 153 }
146 154
147 my-global-icon { 155 my-global-icon {
148 @include apply-svg-color(#fff) 156 @include apply-svg-color(#fff);
149 } 157 }
150} 158}
151 159
@@ -155,7 +163,9 @@
155 border: 2px solid pvar(--mainColor); 163 border: 2px solid pvar(--mainColor);
156 font-weight: $font-semibold; 164 font-weight: $font-semibold;
157 165
158 &, &:active, &:focus { 166 &,
167 &:active,
168 &:focus {
159 color: pvar(--mainColor); 169 color: pvar(--mainColor);
160 background-color: pvar(--mainBackgroundColor); 170 background-color: pvar(--mainBackgroundColor);
161 } 171 }
@@ -165,14 +175,15 @@
165 background-color: pvar(--mainColorLightest); 175 background-color: pvar(--mainColorLightest);
166 } 176 }
167 177
168 &[disabled], &.disabled { 178 &[disabled],
179 &.disabled {
169 cursor: default; 180 cursor: default;
170 color: pvar(--mainColor); 181 color: pvar(--mainColor);
171 background-color: #C6C6C6; 182 background-color: #C6C6C6;
172 } 183 }
173 184
174 my-global-icon { 185 my-global-icon {
175 @include apply-svg-color(pvar(--mainColor)) 186 @include apply-svg-color(pvar(--mainColor));
176 } 187 }
177} 188}
178 189
@@ -182,12 +193,13 @@
182 color: pvar(--greyForegroundColor); 193 color: pvar(--greyForegroundColor);
183 background-color: transparent; 194 background-color: transparent;
184 195
185 &[disabled], &.disabled { 196 &[disabled],
197 .disabled {
186 cursor: default; 198 cursor: default;
187 } 199 }
188 200
189 my-global-icon { 201 my-global-icon {
190 @include apply-svg-color(transparent) 202 @include apply-svg-color(transparent);
191 } 203 }
192} 204}
193 205
@@ -197,17 +209,22 @@
197 background-color: $grey-background-color; 209 background-color: $grey-background-color;
198 color: pvar(--greyForegroundColor); 210 color: pvar(--greyForegroundColor);
199 211
200 &:hover, &:active, &:focus, &[disabled], &.disabled { 212 &:hover,
213 &:active,
214 &:focus,
215 &[disabled],
216 &.disabled {
201 color: pvar(--greyForegroundColor); 217 color: pvar(--greyForegroundColor);
202 background-color: $grey-background-hover-color; 218 background-color: $grey-background-hover-color;
203 } 219 }
204 220
205 &[disabled], &.disabled { 221 &[disabled],
222 &.disabled {
206 cursor: default; 223 cursor: default;
207 } 224 }
208 225
209 my-global-icon { 226 my-global-icon {
210 @include apply-svg-color(pvar(--greyForegroundColor)) 227 @include apply-svg-color(pvar(--greyForegroundColor));
211 } 228 }
212} 229}
213 230
@@ -216,24 +233,30 @@
216 $text: #fff6f5; 233 $text: #fff6f5;
217 234
218 @include button-focus(scale-color($color, $alpha: -95%)); 235 @include button-focus(scale-color($color, $alpha: -95%));
236
219 background-color: $color; 237 background-color: $color;
220 color: $text; 238 color: $text;
221 239
222 &:hover, &:active, &:focus, &[disabled], &.disabled { 240 &:hover,
241 &:active,
242 &:focus,
243 &[disabled],
244 &.disabled {
223 background-color: lighten($color: $color, $amount: 10); 245 background-color: lighten($color: $color, $amount: 10);
224 } 246 }
225 247
226 &[disabled], &.disabled { 248 &[disabled],
249 &.disabled {
227 cursor: default; 250 cursor: default;
228 } 251 }
229 252
230 my-global-icon { 253 my-global-icon {
231 @include apply-svg-color($text) 254 @include apply-svg-color($text);
232 } 255 }
233} 256}
234 257
235@mixin peertube-button { 258@mixin peertube-button {
236 border: none; 259 border: 0;
237 font-weight: $font-semibold; 260 font-weight: $font-semibold;
238 font-size: 15px; 261 font-size: 15px;
239 height: $button-height; 262 height: $button-height;
@@ -246,18 +269,17 @@
246} 269}
247 270
248@mixin peertube-button-link { 271@mixin peertube-button-link {
249 display: inline-block;
250
251 @include disable-default-a-behaviour; 272 @include disable-default-a-behaviour;
252 @include peertube-button; 273 @include peertube-button;
253}
254 274
255@mixin peertube-button-outline {
256 display: inline-block; 275 display: inline-block;
276}
257 277
278@mixin peertube-button-outline {
258 @include disable-default-a-behaviour; 279 @include disable-default-a-behaviour;
259 @include peertube-button; 280 @include peertube-button;
260 281
282 display: inline-block;
261 border: 1px solid; 283 border: 1px solid;
262} 284}
263 285
@@ -291,17 +313,17 @@
291 filter: alpha(opacity=0); 313 filter: alpha(opacity=0);
292 opacity: 0; 314 opacity: 0;
293 outline: none; 315 outline: none;
294 background: white; 316 background: #fff;
295 cursor: inherit; 317 cursor: inherit;
296 display: block; 318 display: block;
297 } 319 }
298} 320}
299 321
300@mixin peertube-button-file ($width) { 322@mixin peertube-button-file ($width) {
301 width: $width;
302
303 @include peertube-file; 323 @include peertube-file;
304 @include peertube-button; 324 @include peertube-button;
325
326 width: $width;
305} 327}
306 328
307@mixin icon ($size) { 329@mixin icon ($size) {
@@ -317,7 +339,7 @@
317@mixin select-arrow-down { 339@mixin select-arrow-down {
318 top: 50%; 340 top: 50%;
319 right: calc(0% + 15px); 341 right: calc(0% + 15px);
320 content: " "; 342 content: ' ';
321 height: 0; 343 height: 0;
322 width: 0; 344 width: 0;
323 position: absolute; 345 position: absolute;
@@ -358,7 +380,7 @@
358 width: 100%; 380 width: 100%;
359 } 381 }
360 382
361 &:after { 383 &::after {
362 @include select-arrow-down; 384 @include select-arrow-down;
363 } 385 }
364 386
@@ -394,21 +416,21 @@
394 option { 416 option {
395 font-weight: $font-semibold; 417 font-weight: $font-semibold;
396 color: pvar(--greyForegroundColor); 418 color: pvar(--greyForegroundColor);
397 border: none; 419 border: 0;
398 } 420 }
399 } 421 }
400} 422}
401 423
402// Thanks: https://codepen.io/triss90/pen/XNEdRe/ 424// Thanks: https://codepen.io/triss90/pen/XNEdRe/
403@mixin peertube-radio-container { 425@mixin peertube-radio-container {
404 input[type="radio"] { 426 input[type=radio] {
405 display: none; 427 display: none;
406 428
407 & + label { 429 + label {
408 font-weight: $font-regular; 430 font-weight: $font-regular;
409 cursor: pointer; 431 cursor: pointer;
410 432
411 &:before { 433 &::before {
412 position: relative; 434 position: relative;
413 top: -2px; 435 top: -2px;
414 content: ''; 436 content: '';
@@ -425,12 +447,12 @@
425 } 447 }
426 } 448 }
427 449
428 &:checked + label:before { 450 &:checked + label::before {
429 background-color: #000; 451 background-color: #000;
430 box-shadow: inset 0 0 0 4px #fff; 452 box-shadow: inset 0 0 0 4px #fff;
431 } 453 }
432 454
433 &:focus + label:before { 455 &:focus + label::before {
434 outline: none; 456 outline: none;
435 border-color: #000; 457 border-color: #000;
436 } 458 }
@@ -445,7 +467,7 @@
445 box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest); 467 box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
446 } 468 }
447 469
448 & + span { 470 + span {
449 position: relative; 471 position: relative;
450 width: 18px; 472 width: 18px;
451 min-width: 18px; 473 min-width: 18px;
@@ -455,7 +477,7 @@
455 vertical-align: middle; 477 vertical-align: middle;
456 cursor: pointer; 478 cursor: pointer;
457 479
458 &:after { 480 &::after {
459 content: ''; 481 content: '';
460 position: absolute; 482 position: absolute;
461 top: calc(2px - #{$border-width}); 483 top: calc(2px - #{$border-width});
@@ -474,13 +496,13 @@
474 background: pvar(--mainColor); 496 background: pvar(--mainColor);
475 animation: jelly 0.6s ease; 497 animation: jelly 0.6s ease;
476 498
477 &:after { 499 &::after {
478 opacity: 1; 500 opacity: 1;
479 transform: rotate(45deg) scale(1); 501 transform: rotate(45deg) scale(1);
480 } 502 }
481 } 503 }
482 504
483 & + span + span { 505 + span + span {
484 font-size: 15px; 506 font-size: 15px;
485 font-weight: $font-regular; 507 font-weight: $font-regular;
486 margin-left: 5px; 508 margin-left: 5px;
@@ -489,7 +511,7 @@
489 } 511 }
490 512
491 &[disabled] + span, 513 &[disabled] + span,
492 &[disabled] + span + span{ 514 &[disabled] + span + span {
493 opacity: 0.5; 515 opacity: 0.5;
494 cursor: default; 516 cursor: default;
495 } 517 }
@@ -539,21 +561,12 @@
539 } 561 }
540} 562}
541 563
542@mixin avatar ($size) { 564@mixin actor-avatar-size ($size) {
543 object-fit: cover; 565 display: inline-block;
544 border-radius: 50%;
545 width: $size;
546 height: $size;
547 min-width: $size;
548 min-height: $size;
549}
550
551@mixin channel-avatar ($size) {
552 width: $size; 566 width: $size;
553 height: $size; 567 height: $size;
554 min-width: $size; 568 min-width: $size;
555 min-height: $size; 569 min-height: $size;
556 border-radius: 5px;
557} 570}
558 571
559@mixin chevron ($size, $border-width) { 572@mixin chevron ($size, $border-width) {
@@ -595,26 +608,6 @@
595 margin-bottom: 10px; 608 margin-bottom: 10px;
596} 609}
597 610
598@mixin actor-owner {
599 @include disable-default-a-behaviour;
600
601 font-size: 13px;
602 margin-top: 4px;
603 color: pvar(--mainForegroundColor);
604
605 span:hover {
606 opacity: 0.8;
607 }
608
609 img {
610 @include avatar(18px);
611
612 margin-left: 7px;
613 position: relative;
614 top: -2px;
615 }
616}
617
618@mixin create-button { 611@mixin create-button {
619 @include peertube-button-link; 612 @include peertube-button-link;
620 @include orange-button; 613 @include orange-button;
@@ -714,13 +707,13 @@
714 color: pvar(--mainColor); 707 color: pvar(--mainColor);
715 } 708 }
716 709
717 & + .breadcrumb-item { 710 + .breadcrumb-item {
718 padding-left: 0.5rem; 711 padding-left: 0.5rem;
719 &::before { 712 &::before {
720 display: inline-block; 713 display: inline-block;
721 padding-right: 0.5rem; 714 padding-right: 0.5rem;
722 color: #6c757d; 715 color: #6c757d;
723 content: "/"; 716 content: '/';
724 } 717 }
725 } 718 }
726 719
@@ -735,13 +728,13 @@
735 flex-wrap: wrap; 728 flex-wrap: wrap;
736 margin: 0 -5px; 729 margin: 0 -5px;
737 730
738 & > div { 731 > div {
739 box-sizing: border-box; 732 box-sizing: border-box;
740 flex: 0 0 percentage(1/3); 733 flex: 0 0 percentage(1/3);
741 padding: 0 5px; 734 padding: 0 5px;
742 margin-bottom: 10px; 735 margin-bottom: 10px;
743 736
744 & > a { 737 > a {
745 @include disable-default-a-behaviour; 738 @include disable-default-a-behaviour;
746 739
747 text-decoration: none; 740 text-decoration: none;
@@ -756,8 +749,8 @@
756 } 749 }
757 } 750 }
758 751
759 & > a, 752 > a,
760 & > div { 753 > div {
761 padding: 20px; 754 padding: 20px;
762 background: pvar(--submenuBackgroundColor); 755 background: pvar(--submenuBackgroundColor);
763 border-radius: 4px; 756 border-radius: 4px;
@@ -766,7 +759,8 @@
766 } 759 }
767 } 760 }
768 761
769 .dashboard-num, .dashboard-text { 762 .dashboard-num,
763 .dashboard-text {
770 text-align: center; 764 text-align: center;
771 font-size: 130%; 765 font-size: 130%;
772 color: pvar(--mainForegroundColor); 766 color: pvar(--mainForegroundColor);
@@ -830,17 +824,9 @@
830 --chip-padding: .2rem .3rem; 824 --chip-padding: .2rem .3rem;
831 } 825 }
832 826
833 .avatar { 827 my-actor-avatar {
834 margin-left: -.4rem; 828 margin-left: -.4rem;
835 margin-right: .2rem; 829 margin-right: .2rem;
836 height: $avatar-height;
837 width: $avatar-height;
838
839 border-radius: 50%;
840 display: inline-block;
841 line-height: 1.25;
842 position: relative;
843 vertical-align: middle;
844 } 830 }
845 831
846 &.two-lines { 832 &.two-lines {
@@ -848,9 +834,8 @@
848 834
849 height: $avatar-height; 835 height: $avatar-height;
850 836
851 .avatar { 837 my-actor-avatar {
852 height: $avatar-height; 838 @include actor-avatar-size($avatar-height);
853 width: $avatar-height;
854 } 839 }
855 840
856 div { 841 div {
@@ -868,7 +853,7 @@
868 flex-direction: column; 853 flex-direction: column;
869 854
870 .form-sub-title { 855 .form-sub-title {
871 margin-right: 0px !important; 856 margin-right: 0 !important;
872 margin-bottom: 10px; 857 margin-bottom: 10px;
873 text-align: center; 858 text-align: center;
874 } 859 }
@@ -914,15 +899,17 @@
914 padding-bottom: 15px; 899 padding-bottom: 15px;
915 margin-bottom: $sub-menu-margin-bottom; 900 margin-bottom: $sub-menu-margin-bottom;
916 901
902 > span > my-global-icon,
917 > my-global-icon { 903 > my-global-icon {
918 margin-right: 10px; 904 margin-right: 10px;
919 vertical-align: bottom;
920 width: 24px; 905 width: 24px;
921 height: 24px; 906 height: 24px;
907 vertical-align: top;
922 } 908 }
923 909
924 .badge { 910 .badge {
925 margin-left: 7px; 911 margin-left: 7px;
912 vertical-align: top;
926 } 913 }
927 } 914 }
928} 915}
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss
index d2a5d2bd9..d54563df6 100644
--- a/client/src/sass/include/_variables.scss
+++ b/client/src/sass/include/_variables.scss
@@ -60,7 +60,7 @@ $max-channels-width: 1200px;
60$footer-height: 30px; 60$footer-height: 30px;
61$footer-margin: 30px; 61$footer-margin: 30px;
62 62
63$separator-border-color: rgba(0, 0, 0, 0.10); 63$separator-border-color: rgba(0, 0, 0, 0.1);
64 64
65$video-miniature-margin-bottom: 15px; 65$video-miniature-margin-bottom: 15px;
66 66
@@ -90,7 +90,7 @@ $markdown-textarea-background-color: $grey-background-hover-color;
90$sub-menu-margin-bottom: 30px; 90$sub-menu-margin-bottom: 30px;
91$sub-menu-margin-bottom-small-view: 10px; 91$sub-menu-margin-bottom-small-view: 10px;
92 92
93$activated-action-button-color: black; 93$activated-action-button-color: #000;
94 94
95$focus-box-shadow-form: 0 0 0 .2rem; 95$focus-box-shadow-form: 0 0 0 .2rem;
96 96
@@ -147,7 +147,7 @@ $variables: (
147 @if map-has-key($variables, $variable) { 147 @if map-has-key($variables, $variable) {
148 @return map-get($variables, $variable); 148 @return map-get($variables, $variable);
149 } @else { 149 } @else {
150 @error "ERROR: Variable #{$variable} does not exist"; 150 @error 'ERROR: Variable #{$variable} does not exist';
151 } 151 }
152} 152}
153 153
diff --git a/client/src/sass/ng-select.scss b/client/src/sass/ng-select.scss
index 61da6d266..13b2012b2 100644
--- a/client/src/sass/ng-select.scss
+++ b/client/src/sass/ng-select.scss
@@ -14,7 +14,7 @@ $ng-select-height: 30px;
14$ng-select-value-padding-left: 15px; 14$ng-select-value-padding-left: 15px;
15$ng-select-value-font-size: 15px; 15$ng-select-value-font-size: 15px;
16 16
17@import "~@ng-select/ng-select/scss/default.theme.scss"; 17@import '~@ng-select/ng-select/scss/default.theme';
18 18
19.ng-select { 19.ng-select {
20 font-size: $ng-select-value-font-size; 20 font-size: $ng-select-value-font-size;
@@ -31,13 +31,13 @@ $ng-select-value-font-size: 15px;
31 } 31 }
32 32
33 .ng-arrow-wrapper { 33 .ng-arrow-wrapper {
34 padding-right: 12px 34 padding-right: 12px;
35 } 35 }
36 36
37 &.ng-select-single .ng-value-container .ng-value { 37 &.ng-select-single .ng-value-container .ng-value {
38 color: pvar(--inputForegroundColor); 38 color: pvar(--inputForegroundColor);
39 39
40 .ng-value-label { 40 .ng-value-label { /* stylelint-disable-line */
41 display: flex; 41 display: flex;
42 align-items: center; 42 align-items: center;
43 } 43 }
@@ -45,7 +45,8 @@ $ng-select-value-font-size: 15px;
45 45
46 &.ng-select-multiple .ng-select-container .ng-value-container { 46 &.ng-select-multiple .ng-select-container .ng-value-container {
47 padding-left: 12px; 47 padding-left: 12px;
48 .ng-value { 48
49 .ng-value { /* stylelint-disable-line */
49 margin-left: 3px; 50 margin-left: 3px;
50 } 51 }
51 } 52 }
diff --git a/client/src/sass/player/context-menu.scss b/client/src/sass/player/context-menu.scss
index df78916c6..45cee3e77 100644
--- a/client/src/sass/player/context-menu.scss
+++ b/client/src/sass/player/context-menu.scss
@@ -8,7 +8,7 @@ $context-menu-width: 350px;
8 8
9.video-js .vjs-contextmenu-ui-menu { 9.video-js .vjs-contextmenu-ui-menu {
10 position: absolute; 10 position: absolute;
11 background-color: rgba(0, 0, 0, 0.5); 11 background-color: $primary-background-color;
12 padding: 8px 0; 12 padding: 8px 0;
13 border-radius: 4px; 13 border-radius: 4px;
14 width: $context-menu-width; 14 width: $context-menu-width;
@@ -31,26 +31,26 @@ $context-menu-width: 350px;
31 background-color: rgba(255, 255, 255, 0.2); 31 background-color: rgba(255, 255, 255, 0.2);
32 } 32 }
33 33
34 [class^="vjs-icon-"] { 34 [class^='vjs-icon-'] {
35 $icons: 'link-2', 'repeat', 'code', 'tick-white', 'info';
36
35 display: inline-flex; 37 display: inline-flex;
36 position: relative; 38 position: relative;
37 top: 2px; 39 top: 2px;
38 cursor: pointer; 40 cursor: pointer;
39 width: 14px; 41 width: 14px;
40 height: 14px; 42 height: 14px;
41 background-color: white; 43 background-color: #fff;
42 mask-size: cover; 44 mask-size: cover;
43 margin-right: 0.8rem !important; 45 margin-right: 0.8rem !important;
44 46
45 $icons: 'link-2', 'repeat', 'code', 'tick-white';
46
47 @each $icon in $icons { 47 @each $icon in $icons {
48 &[class$="-#{$icon}"] { 48 &[class$="-#{$icon}"] {
49 mask-image: url('#{$assets-path}/player/images/#{$icon}.svg'); 49 mask-image: url('#{$assets-path}/player/images/#{$icon}.svg');
50 } 50 }
51 } 51 }
52 52
53 &[class$="-tick-white"] { 53 &[class$='-tick-white'] {
54 float: right; 54 float: right;
55 margin: 0 !important; 55 margin: 0 !important;
56 } 56 }
diff --git a/client/src/sass/player/index.scss b/client/src/sass/player/index.scss
index fe92ce5e0..e674fa2f6 100644
--- a/client/src/sass/player/index.scss
+++ b/client/src/sass/player/index.scss
@@ -4,5 +4,6 @@
4@import './settings-menu'; 4@import './settings-menu';
5@import './spinner'; 5@import './spinner';
6@import './upnext'; 6@import './upnext';
7@import './bezels.scss'; 7@import './bezels';
8@import './playlist.scss'; 8@import './playlist';
9@import './stats';
diff --git a/client/src/sass/player/mobile.scss b/client/src/sass/player/mobile.scss
index c2fa855ab..26066d218 100644
--- a/client/src/sass/player/mobile.scss
+++ b/client/src/sass/player/mobile.scss
@@ -13,4 +13,4 @@
13 } 13 }
14 } 14 }
15 } 15 }
16} \ No newline at end of file 16}
diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss
index 81aacf1d7..8fe2e054d 100644
--- a/client/src/sass/player/peertube-skin.scss
+++ b/client/src/sass/player/peertube-skin.scss
@@ -52,12 +52,12 @@ body {
52 } 52 }
53 53
54 .vjs-big-play-button { 54 .vjs-big-play-button {
55 outline: 0;
56 font-size: 6em;
57
58 $big-play-width: 1.2em; 55 $big-play-width: 1.2em;
59 $big-play-height: 1.2em; 56 $big-play-height: 1.2em;
60 57
58 outline: 0;
59 font-size: 6em;
60
61 border: 2px solid #fff; 61 border: 2px solid #fff;
62 border-radius: 100%; 62 border-radius: 100%;
63 63
@@ -72,7 +72,7 @@ body {
72 72
73 &::-moz-focus-inner { 73 &::-moz-focus-inner {
74 border: 0; 74 border: 0;
75 padding: 0 75 padding: 0;
76 } 76 }
77 77
78 .vjs-icon-placeholder::before { 78 .vjs-icon-placeholder::before {
@@ -82,8 +82,9 @@ body {
82 background-image: url('#{$assets-path}/player/images/big-play-button.svg'); 82 background-image: url('#{$assets-path}/player/images/big-play-button.svg');
83 } 83 }
84 84
85 &.focus-visible, &:hover { 85 &.focus-visible,
86 background-color: var(--mainColor, dimgray); 86 &:hover {
87 background-color: var(--mainColor, #696969);
87 } 88 }
88 89
89 } 90 }
@@ -91,16 +92,19 @@ body {
91 // Small effect when we click on the play button 92 // Small effect when we click on the play button
92 &.vjs-has-big-play-button-clicked { 93 &.vjs-has-big-play-button-clicked {
93 94
94 .vjs-big-play-button, .vjs-poster { 95 .vjs-big-play-button,
96 .vjs-poster {
95 display: block; 97 display: block;
96 visibility: hidden; 98 visibility: hidden;
97 99
98 &.vjs-big-play-button, &.vjs-big-play-button::before { 100 &.vjs-big-play-button,
101 &.vjs-big-play-button::before {
99 opacity: 0; 102 opacity: 0;
100 transition: visibility 0.2s, opacity 0.2s; 103 transition: visibility 0.2s, opacity 0.2s;
101 } 104 }
102 105
103 &.vjs-poster, &.vjs-poster::before { 106 &.vjs-poster,
107 &.vjs-poster::before {
104 opacity: 0; 108 opacity: 0;
105 transition: visibility 0.3s, opacity 0.3s; 109 transition: visibility 0.3s, opacity 0.3s;
106 transition-delay: 0.05s; 110 transition-delay: 0.05s;
@@ -165,8 +169,7 @@ body {
165 .vjs-fullscreen-control, 169 .vjs-fullscreen-control,
166 .vjs-peertube-link, 170 .vjs-peertube-link,
167 .vjs-theater-control, 171 .vjs-theater-control,
168 .vjs-settings 172 .vjs-settings {
169 {
170 color: pvar(--embedForegroundColor) !important; 173 color: pvar(--embedForegroundColor) !important;
171 174
172 opacity: $primary-foreground-opacity; 175 opacity: $primary-foreground-opacity;
@@ -217,7 +220,8 @@ body {
217 } 220 }
218 221
219 .vjs-load-progress { 222 .vjs-load-progress {
220 &, & div { 223 &,
224 div {
221 background: rgba(255, 255, 255, .2); 225 background: rgba(255, 255, 255, .2);
222 } 226 }
223 } 227 }
@@ -266,7 +270,7 @@ body {
266 line-height: calc(#{$control-bar-height} - 1px); 270 line-height: calc(#{$control-bar-height} - 1px);
267 271
268 &::after { 272 &::after {
269 content: "/"; 273 content: '/';
270 margin: 0 1px 0 2px; 274 margin: 0 1px 0 2px;
271 } 275 }
272 } 276 }
@@ -308,11 +312,17 @@ body {
308 display: none; 312 display: none;
309 } 313 }
310 314
311 .download-speed-number, .upload-speed-number, .peers-number, .http-fallback { 315 .download-speed-number,
316 .upload-speed-number,
317 .peers-number,
318 .http-fallback {
312 font-weight: $font-semibold; 319 font-weight: $font-semibold;
313 } 320 }
314 321
315 .download-speed-text, .upload-speed-text, .peers-text, .http-fallback { 322 .download-speed-text,
323 .upload-speed-text,
324 .peers-text,
325 .http-fallback {
316 margin-right: 15px; 326 margin-right: 15px;
317 } 327 }
318 328
@@ -336,10 +346,8 @@ body {
336 &.icon-next, 346 &.icon-next,
337 &.icon-previous { 347 &.icon-previous {
338 mask-image: url('#{$assets-path}/player/images/next.svg'); 348 mask-image: url('#{$assets-path}/player/images/next.svg');
339 -webkit-mask-image: url('#{$assets-path}/player/images/next.svg'); 349 background-color: #fff;
340 background-color: white;
341 mask-size: cover; 350 mask-size: cover;
342 -webkit-mask-size: cover;
343 width: 11px; 351 width: 11px;
344 height: 11px; 352 height: 11px;
345 margin-top: -2px; 353 margin-top: -2px;
@@ -410,7 +418,7 @@ body {
410 } 418 }
411 419
412 .vjs-volume-bar { 420 .vjs-volume-bar {
413 background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcCAQAAACw95UnAAAAMElEQVRIx2NgoBL4n4YKGUYNHkEG4zJg1OCRYDCpBowaPJwMppbLRg0eNXjUYBLEAXWNUA6QNm1lAAAAAElFTkSuQmCC) no-repeat; 421 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcCAQAAACw95UnAAAAMElEQVRIx2NgoBL4n4YKGUYNHkEG4zJg1OCRYDCpBowaPJwMppbLRg0eNXjUYBLEAXWNUA6QNm1lAAAAAElFTkSuQmCC') no-repeat;
414 background-size: 22px 14px; 422 background-size: 22px 14px;
415 height: 100%; 423 height: 100%;
416 width: 100%; 424 width: 100%;
@@ -421,7 +429,7 @@ body {
421 top: 3px; 429 top: 3px;
422 430
423 .vjs-volume-level { 431 .vjs-volume-level {
424 background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcAQAAAAAyhWABAAAAAnRSTlMAAHaTzTgAAAAZSURBVHgBYwAB/g9EUv+JokCqiaT+U4MCAPKPS7WUUOc1AAAAAElFTkSuQmCC) no-repeat; 432 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcAQAAAAAyhWABAAAAAnRSTlMAAHaTzTgAAAAZSURBVHgBYwAB/g9EUv+JokCqiaT+U4MCAPKPS7WUUOc1AAAAAElFTkSuQmCC') no-repeat;
425 background-size: 22px 14px; 433 background-size: 22px 14px;
426 max-width: 22px; 434 max-width: 22px;
427 max-height: 14px; 435 max-height: 14px;
diff --git a/client/src/sass/player/playlist.scss b/client/src/sass/player/playlist.scss
index ebbed02d9..8558fc837 100644
--- a/client/src/sass/player/playlist.scss
+++ b/client/src/sass/player/playlist.scss
@@ -44,10 +44,8 @@ $playlist-menu-width: 350px;
44 width: 20px; 44 width: 20px;
45 height: 20px; 45 height: 20px;
46 mask-image: url('#{$assets-path}/images/feather/x.svg'); 46 mask-image: url('#{$assets-path}/images/feather/x.svg');
47 -webkit-mask-image: url('#{$assets-path}/images/feather/x.svg'); 47 background-color: #fff;
48 background-color: white;
49 mask-size: cover; 48 mask-size: cover;
50 -webkit-mask-size: cover;
51 } 49 }
52 } 50 }
53} 51}
@@ -90,10 +88,8 @@ $playlist-menu-width: 350px;
90 width: 22px; 88 width: 22px;
91 height: 22px; 89 height: 22px;
92 mask-image: url('#{$assets-path}/images/feather/list.svg'); 90 mask-image: url('#{$assets-path}/images/feather/list.svg');
93 -webkit-mask-image: url('#{$assets-path}/images/feather/list.svg'); 91 background-color: #fff;
94 background-color: white;
95 mask-size: cover; 92 mask-size: cover;
96 -webkit-mask-size: cover;
97 margin-bottom: 3px; 93 margin-bottom: 3px;
98} 94}
99 95
@@ -133,9 +129,9 @@ $playlist-menu-width: 350px;
133 } 129 }
134 130
135 .item-player { 131 .item-player {
136 display: none;
137
138 @include play-icon(20px, 16px); 132 @include play-icon(20px, 16px);
133
134 display: none;
139 } 135 }
140 136
141 &.vjs-selected { 137 &.vjs-selected {
diff --git a/client/src/sass/player/settings-menu.scss b/client/src/sass/player/settings-menu.scss
index 09c872ef7..74eee7d64 100644
--- a/client/src/sass/player/settings-menu.scss
+++ b/client/src/sass/player/settings-menu.scss
@@ -149,7 +149,7 @@ $setting-transition-easing: ease-out;
149 background-color: inherit; 149 background-color: inherit;
150 padding: 8px 8px 13px 12px; 150 padding: 8px 8px 13px 12px;
151 margin-bottom: 5px; 151 margin-bottom: 5px;
152 border-bottom: 1px solid grey; 152 border-bottom: 1px solid #808080;
153 text-align: left; 153 text-align: left;
154 154
155 &::before { 155 &::before {
diff --git a/client/src/sass/player/spinner.scss b/client/src/sass/player/spinner.scss
index a6af8da33..94f4d1008 100644
--- a/client/src/sass/player/spinner.scss
+++ b/client/src/sass/player/spinner.scss
@@ -51,4 +51,4 @@
51 opacity: 1; 51 opacity: 1;
52 } 52 }
53 } 53 }
54} \ No newline at end of file 54}
diff --git a/client/src/sass/player/stats.scss b/client/src/sass/player/stats.scss
new file mode 100644
index 000000000..6fcbcd969
--- /dev/null
+++ b/client/src/sass/player/stats.scss
@@ -0,0 +1,41 @@
1@import './_player-variables';
2
3$stats-width: 420px;
4$contextmenu-background-color: rgba(0, 0, 0, 0.6);
5
6.video-js {
7
8 .vjs-stats-content {
9 @include transition(opacity 0.1s);
10
11 position: absolute;
12 background-color: $contextmenu-background-color;
13 padding: 5px 0;
14 border-radius: 4px;
15 width: $stats-width;
16 min-width: 27em;
17 max-width: calc(100vw - 20px);
18 left: 10px;
19 top: 10px;
20 z-index: 64;
21 font-size: 12px;
22 line-height: 1.2;
23 }
24
25 .vjs-stats-close {
26 cursor: pointer;
27 position: absolute;
28 right: 3px;
29 top: 3px;
30 padding: 0;
31 }
32
33 .vjs-stats-list > div > div {
34 display: inline-block;
35 font-weight: 600;
36 padding: 0 .5em;
37 text-align: right;
38 width: 11.5em;
39 white-space: nowrap;
40 }
41}
diff --git a/client/src/sass/player/upnext.scss b/client/src/sass/player/upnext.scss
index 7614bb3b6..8c9a6f784 100644
--- a/client/src/sass/player/upnext.scss
+++ b/client/src/sass/player/upnext.scss
@@ -11,15 +11,15 @@ $browser-context: 16;
11.video-js { 11.video-js {
12 12
13 .vjs-upnext-content { 13 .vjs-upnext-content {
14 @include transition(opacity 0.1s);
15
14 font-size: 1.8em; 16 font-size: 1.8em;
15 pointer-events: auto; 17 pointer-events: auto;
16 position: absolute; 18 position: absolute;
17 top: 0; 19 top: 0;
18 bottom: 0; 20 bottom: 0;
19 background: rgba(0,0,0,0.6); 21 background: rgba(0, 0, 0, 0.6);
20 width: 100%; 22 width: 100%;
21
22 @include transition(opacity 0.1s);
23 } 23 }
24 24
25 .vjs-upnext-top { 25 .vjs-upnext-top {
@@ -77,7 +77,7 @@ $browser-context: 16;
77 float: none; 77 float: none;
78 padding: 10px !important; 78 padding: 10px !important;
79 font-size: 16px !important; 79 font-size: 16px !important;
80 border: none; 80 border: 0;
81 } 81 }
82 82
83 .vjs-upnext-cancel-button, 83 .vjs-upnext-cancel-button,
@@ -86,7 +86,7 @@ $browser-context: 16;
86 } 86 }
87 87
88 .vjs-upnext-cancel-button:hover { 88 .vjs-upnext-cancel-button:hover {
89 background-color: rgba(255,255,255,0.25); 89 background-color: rgba(255, 255, 255, 0.25);
90 border-radius: 2px; 90 border-radius: 2px;
91 } 91 }
92 92
@@ -95,6 +95,8 @@ $browser-context: 16;
95 } 95 }
96 96
97 .vjs-upnext-autoplay-icon { 97 .vjs-upnext-autoplay-icon {
98 @include transition(stroke-dasharray 0.1s cubic-bezier(0.4,0,1,1));
99
98 position: absolute; 100 position: absolute;
99 top: 50%; 101 top: 50%;
100 left: 50%; 102 left: 50%;
@@ -102,8 +104,6 @@ $browser-context: 16;
102 height: 98px; 104 height: 98px;
103 margin: -49px 0 0 -49px; 105 margin: -49px 0 0 -49px;
104 cursor: pointer; 106 cursor: pointer;
105
106 @include transition(stroke-dasharray 0.1s cubic-bezier(0.4,0,1,1));
107 } 107 }
108 108
109} 109}
diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss
index 544d0039a..6a4d89dff 100644
--- a/client/src/sass/primeng-custom.scss
+++ b/client/src/sass/primeng-custom.scss
@@ -1,13 +1,18 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4/* stylelint-disable */
4@import '~primeng/resources/primeng.css'; 5@import '~primeng/resources/primeng.css';
5 6
6// Override primeng style we don't want 7// Override primeng style we don't want
7input[type="button"] { 8input[type=button] {
8 border-radius: inherit; 9 border-radius: inherit;
9} 10}
10 11
12p-table .p-datatable-header .caption {
13 margin-bottom: 15px;
14}
15
11// Taken from old nova light theme 16// Taken from old nova light theme
12 17
13body .p-disabled { 18body .p-disabled {
@@ -511,10 +516,6 @@ p-table {
511 .left-buttons { 516 .left-buttons {
512 padding-left: 15px; 517 padding-left: 15px;
513 } 518 }
514
515 .input-group-text {
516 background-color: transparent;
517 }
518 } 519 }
519 } 520 }
520 521
diff --git a/client/src/standalone/videos/embed.scss b/client/src/standalone/videos/embed.scss
index cbe6bdd01..e32cce54e 100644
--- a/client/src/standalone/videos/embed.scss
+++ b/client/src/standalone/videos/embed.scss
@@ -21,7 +21,8 @@ video {
21} 21}
22 22
23/* fill the entire space */ 23/* fill the entire space */
24html, body { 24html,
25body {
25 height: 100%; 26 height: 100%;
26 margin: 0; 27 margin: 0;
27 background-color: #000; 28 background-color: #000;
@@ -70,18 +71,18 @@ html, body {
70 text-align: center; 71 text-align: center;
71 width: 100%; 72 width: 100%;
72 height: 100%; 73 height: 100%;
73 color: white; 74 color: #fff;
74 box-sizing: border-box; 75 box-sizing: border-box;
75 font-family: sans-serif; 76 font-family: sans-serif;
77}
76 78
77 #error-title { 79#error-title {
78 font-size: 45px; 80 font-size: 45px;
79 margin-bottom: 5px; 81 margin-bottom: 5px;
80 } 82}
81 83
82 #error-content { 84#error-content {
83 font-size: 24px; 85 font-size: 24px;
84 }
85} 86}
86 87
87#placeholder-preview { 88#placeholder-preview {
@@ -97,10 +98,10 @@ html, body {
97@media screen and (max-width: 300px) { 98@media screen and (max-width: 300px) {
98 #error-block { 99 #error-block {
99 font-size: 36px; 100 font-size: 36px;
101 }
100 102
101 #error-content { 103 #error-content {
102 font-size: 14px; 104 font-size: 14px;
103 }
104 } 105 }
105} 106}
106 107
diff --git a/client/src/standalone/videos/test-embed.scss b/client/src/standalone/videos/test-embed.scss
index 85ce4e0f7..b9ac3e74e 100644
--- a/client/src/standalone/videos/test-embed.scss
+++ b/client/src/standalone/videos/test-embed.scss
@@ -15,7 +15,7 @@ body {
15} 15}
16 16
17iframe { 17iframe {
18 border: none; 18 border: 0;
19 border-radius: 8px; 19 border-radius: 8px;
20 min-width: 200px; 20 min-width: 200px;
21 width: 100%; 21 width: 100%;
@@ -41,7 +41,7 @@ aside {
41 .icon { 41 .icon {
42 height: 100%; 42 height: 100%;
43 padding: 0 18px 0 32px; 43 padding: 0 18px 0 32px;
44 background: white; 44 background: #fff;
45 display: flex; 45 display: flex;
46 align-items: center; 46 align-items: center;
47 margin-right: 0.5em; 47 margin-right: 0.5em;
@@ -62,13 +62,13 @@ header {
62 width: 100%; 62 width: 100%;
63 height: 3.2em; 63 height: 3.2em;
64 background-color: #F1680D; 64 background-color: #F1680D;
65 color: white; 65 color: #fff;
66 //background-image: url(../../assets/images/backdrop/network-o.png); 66 //background-image: url(../../assets/images/backdrop/network-o.png);
67 display: flex; 67 display: flex;
68 flex-direction: row; 68 flex-direction: row;
69 align-items: center; 69 align-items: center;
70 margin-bottom: 1em; 70 margin-bottom: 1em;
71 box-shadow: 1px 0px 10px rgba(0,0,0,0.6); 71 box-shadow: 1px 0 10px rgba(0, 0, 0, 0.6);
72 background-size: 50%; 72 background-size: 50%;
73 background-position: top left; 73 background-position: top left;
74 padding-right: 1em; 74 padding-right: 1em;
@@ -87,13 +87,13 @@ header {
87 display: flex; 87 display: flex;
88 flex-wrap: wrap; 88 flex-wrap: wrap;
89 89
90 & > * { 90 > * {
91 flex-grow: 0; 91 flex-grow: 0;
92 } 92 }
93} 93}
94 94
95fieldset { 95fieldset {
96 border: none; 96 border: 0;
97 min-width: 8em; 97 min-width: 8em;
98 legend { 98 legend {
99 border-bottom: 1px solid #ccc; 99 border-bottom: 1px solid #ccc;
@@ -103,12 +103,12 @@ fieldset {
103 103
104button { 104button {
105 background: #F1680D; 105 background: #F1680D;
106 color: white; 106 color: #fff;
107 font-weight: bold; 107 font-weight: bold;
108 border-radius: 5px; 108 border-radius: 5px;
109 margin: 0; 109 margin: 0;
110 padding: 1em 1.25em; 110 padding: 1em 1.25em;
111 border: none; 111 border: 0;
112} 112}
113 113
114a { 114a {
@@ -118,7 +118,11 @@ a {
118 text-decoration: underline; 118 text-decoration: underline;
119 } 119 }
120 120
121 &, &:hover, &:focus, &:visited, &:active { 121 &,
122 &:hover,
123 &:focus,
124 &:visited,
125 &:active {
122 color: #F44336; 126 color: #F44336;
123 } 127 }
124} 128}
diff --git a/client/yarn.lock b/client/yarn.lock
index 8cd172042..571314f22 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -276,7 +276,7 @@
276 dependencies: 276 dependencies:
277 "@babel/highlight" "^7.12.13" 277 "@babel/highlight" "^7.12.13"
278 278
279"@babel/compat-data@^7.12.7", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8": 279"@babel/compat-data@^7.12.7", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.13.8":
280 version "7.13.15" 280 version "7.13.15"
281 resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.15.tgz#7e8eea42d0b64fda2b375b22d06c605222e848f4" 281 resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.15.tgz#7e8eea42d0b64fda2b375b22d06c605222e848f4"
282 integrity sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA== 282 integrity sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==
@@ -323,6 +323,27 @@
323 semver "^5.4.1" 323 semver "^5.4.1"
324 source-map "^0.5.0" 324 source-map "^0.5.0"
325 325
326"@babel/core@>=7.9.0":
327 version "7.13.16"
328 resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.16.tgz#7756ab24396cc9675f1c3fcd5b79fcce192ea96a"
329 integrity sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q==
330 dependencies:
331 "@babel/code-frame" "^7.12.13"
332 "@babel/generator" "^7.13.16"
333 "@babel/helper-compilation-targets" "^7.13.16"
334 "@babel/helper-module-transforms" "^7.13.14"
335 "@babel/helpers" "^7.13.16"
336 "@babel/parser" "^7.13.16"
337 "@babel/template" "^7.12.13"
338 "@babel/traverse" "^7.13.15"
339 "@babel/types" "^7.13.16"
340 convert-source-map "^1.7.0"
341 debug "^4.1.0"
342 gensync "^1.0.0-beta.2"
343 json5 "^2.1.2"
344 semver "^6.3.0"
345 source-map "^0.5.0"
346
326"@babel/core@^7.7.5", "@babel/core@^7.8.6": 347"@babel/core@^7.7.5", "@babel/core@^7.8.6":
327 version "7.13.15" 348 version "7.13.15"
328 resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.15.tgz#a6d40917df027487b54312202a06812c4f7792d0" 349 resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.15.tgz#a6d40917df027487b54312202a06812c4f7792d0"
@@ -362,6 +383,15 @@
362 jsesc "^2.5.1" 383 jsesc "^2.5.1"
363 source-map "^0.5.0" 384 source-map "^0.5.0"
364 385
386"@babel/generator@^7.13.16":
387 version "7.13.16"
388 resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.16.tgz#0befc287031a201d84cdfc173b46b320ae472d14"
389 integrity sha512-grBBR75UnKOcUWMp8WoDxNsWCFl//XCK6HWTrBQKTr5SV9f5g0pNOjdyzi/DTBv12S9GnYPInIXQBTky7OXEMg==
390 dependencies:
391 "@babel/types" "^7.13.16"
392 jsesc "^2.5.1"
393 source-map "^0.5.0"
394
365"@babel/helper-annotate-as-pure@^7.12.13": 395"@babel/helper-annotate-as-pure@^7.12.13":
366 version "7.12.13" 396 version "7.12.13"
367 resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" 397 resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab"
@@ -387,6 +417,16 @@
387 browserslist "^4.14.5" 417 browserslist "^4.14.5"
388 semver "^6.3.0" 418 semver "^6.3.0"
389 419
420"@babel/helper-compilation-targets@^7.13.16":
421 version "7.13.16"
422 resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c"
423 integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==
424 dependencies:
425 "@babel/compat-data" "^7.13.15"
426 "@babel/helper-validator-option" "^7.12.17"
427 browserslist "^4.14.5"
428 semver "^6.3.0"
429
390"@babel/helper-create-class-features-plugin@^7.13.0": 430"@babel/helper-create-class-features-plugin@^7.13.0":
391 version "7.13.11" 431 version "7.13.11"
392 resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" 432 resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6"
@@ -546,6 +586,15 @@
546 "@babel/traverse" "^7.13.0" 586 "@babel/traverse" "^7.13.0"
547 "@babel/types" "^7.13.0" 587 "@babel/types" "^7.13.0"
548 588
589"@babel/helpers@^7.13.16":
590 version "7.13.17"
591 resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.17.tgz#b497c7a00e9719d5b613b8982bda6ed3ee94caf6"
592 integrity sha512-Eal4Gce4kGijo1/TGJdqp3WuhllaMLSrW6XcL0ulyUAQOuxHcCafZE8KHg9857gcTehsm/v7RcOx2+jp0Ryjsg==
593 dependencies:
594 "@babel/template" "^7.12.13"
595 "@babel/traverse" "^7.13.17"
596 "@babel/types" "^7.13.17"
597
549"@babel/highlight@^7.12.13": 598"@babel/highlight@^7.12.13":
550 version "7.13.10" 599 version "7.13.10"
551 resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" 600 resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1"
@@ -560,6 +609,11 @@
560 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8" 609 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8"
561 integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ== 610 integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==
562 611
612"@babel/parser@^7.13.16":
613 version "7.13.16"
614 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.16.tgz#0f18179b0448e6939b1f3f5c4c355a3a9bcdfd37"
615 integrity sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw==
616
563"@babel/plugin-proposal-async-generator-functions@^7.12.1": 617"@babel/plugin-proposal-async-generator-functions@^7.12.1":
564 version "7.13.15" 618 version "7.13.15"
565 resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz#80e549df273a3b3050431b148c892491df1bcc5b" 619 resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz#80e549df273a3b3050431b148c892491df1bcc5b"
@@ -1148,6 +1202,20 @@
1148 debug "^4.1.0" 1202 debug "^4.1.0"
1149 globals "^11.1.0" 1203 globals "^11.1.0"
1150 1204
1205"@babel/traverse@^7.13.17":
1206 version "7.13.17"
1207 resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.17.tgz#c85415e0c7d50ac053d758baec98b28b2ecfeea3"
1208 integrity sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg==
1209 dependencies:
1210 "@babel/code-frame" "^7.12.13"
1211 "@babel/generator" "^7.13.16"
1212 "@babel/helper-function-name" "^7.12.13"
1213 "@babel/helper-split-export-declaration" "^7.12.13"
1214 "@babel/parser" "^7.13.16"
1215 "@babel/types" "^7.13.17"
1216 debug "^4.1.0"
1217 globals "^11.1.0"
1218
1151"@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6": 1219"@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6":
1152 version "7.13.14" 1220 version "7.13.14"
1153 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" 1221 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d"
@@ -1157,6 +1225,14 @@
1157 lodash "^4.17.19" 1225 lodash "^4.17.19"
1158 to-fast-properties "^2.0.0" 1226 to-fast-properties "^2.0.0"
1159 1227
1228"@babel/types@^7.13.16", "@babel/types@^7.13.17":
1229 version "7.13.17"
1230 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.17.tgz#48010a115c9fba7588b4437dd68c9469012b38b4"
1231 integrity sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA==
1232 dependencies:
1233 "@babel/helper-validator-identifier" "^7.12.11"
1234 to-fast-properties "^2.0.0"
1235
1160"@discoveryjs/json-ext@0.5.2", "@discoveryjs/json-ext@^0.5.0": 1236"@discoveryjs/json-ext@0.5.2", "@discoveryjs/json-ext@^0.5.0":
1161 version "0.5.2" 1237 version "0.5.2"
1162 resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" 1238 resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752"
@@ -1369,6 +1445,21 @@
1369 semver "7.3.4" 1445 semver "7.3.4"
1370 semver-intersect "1.4.0" 1446 semver-intersect "1.4.0"
1371 1447
1448"@stylelint/postcss-css-in-js@^0.37.2":
1449 version "0.37.2"
1450 resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz#7e5a84ad181f4234a2480803422a47b8749af3d2"
1451 integrity sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA==
1452 dependencies:
1453 "@babel/core" ">=7.9.0"
1454
1455"@stylelint/postcss-markdown@^0.36.2":
1456 version "0.36.2"
1457 resolved "https://registry.yarnpkg.com/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz#0a540c4692f8dcdfc13c8e352c17e7bfee2bb391"
1458 integrity sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==
1459 dependencies:
1460 remark "^13.0.0"
1461 unist-util-find-all-after "^3.0.2"
1462
1372"@tootallnate/once@1": 1463"@tootallnate/once@1":
1373 version "1.1.2" 1464 version "1.1.2"
1374 resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" 1465 resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@@ -1503,6 +1594,13 @@
1503 "@types/linkify-it" "*" 1594 "@types/linkify-it" "*"
1504 "@types/mdurl" "*" 1595 "@types/mdurl" "*"
1505 1596
1597"@types/mdast@^3.0.0":
1598 version "3.0.3"
1599 resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb"
1600 integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==
1601 dependencies:
1602 "@types/unist" "*"
1603
1506"@types/mdurl@*": 1604"@types/mdurl@*":
1507 version "1.0.2" 1605 version "1.0.2"
1508 resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" 1606 resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
@@ -1513,6 +1611,11 @@
1513 resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" 1611 resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
1514 integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== 1612 integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
1515 1613
1614"@types/minimist@^1.2.0":
1615 version "1.2.1"
1616 resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256"
1617 integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
1618
1516"@types/mousetrap@1.6.3", "@types/mousetrap@^1.6.0": 1619"@types/mousetrap@1.6.3", "@types/mousetrap@^1.6.0":
1517 version "1.6.3" 1620 version "1.6.3"
1518 resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.3.tgz#3159a01a2b21c9155a3d8f85588885d725dc987d" 1621 resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.3.tgz#3159a01a2b21c9155a3d8f85588885d725dc987d"
@@ -1523,6 +1626,11 @@
1523 resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" 1626 resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e"
1524 integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== 1627 integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==
1525 1628
1629"@types/normalize-package-data@^2.4.0":
1630 version "2.4.0"
1631 resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
1632 integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
1633
1526"@types/parse-json@^4.0.0": 1634"@types/parse-json@^4.0.0":
1527 version "4.0.0" 1635 version "4.0.0"
1528 resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" 1636 resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -1616,6 +1724,11 @@
1616 dependencies: 1724 dependencies:
1617 source-map "^0.6.1" 1725 source-map "^0.6.1"
1618 1726
1727"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2":
1728 version "2.0.3"
1729 resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
1730 integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
1731
1619"@types/video.js@^7.3.8": 1732"@types/video.js@^7.3.8":
1620 version "7.3.15" 1733 version "7.3.15"
1621 resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.3.15.tgz#42ff1bf598384ae5966bf6f64560197357e99033" 1734 resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.3.15.tgz#42ff1bf598384ae5966bf6f64560197357e99033"
@@ -1893,28 +2006,11 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
1893 mime-types "~2.1.24" 2006 mime-types "~2.1.24"
1894 negotiator "0.6.2" 2007 negotiator "0.6.2"
1895 2008
1896acorn-jsx@^3.0.0:
1897 version "3.0.1"
1898 resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
1899 integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=
1900 dependencies:
1901 acorn "^3.0.4"
1902
1903acorn-walk@^8.0.0: 2009acorn-walk@^8.0.0:
1904 version "8.0.2" 2010 version "8.0.2"
1905 resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3" 2011 resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3"
1906 integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A== 2012 integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A==
1907 2013
1908acorn@^3.0.4:
1909 version "3.3.0"
1910 resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
1911 integrity sha1-ReN/s56No/JbruP/U2niu18iAXo=
1912
1913acorn@^5.5.0:
1914 version "5.7.4"
1915 resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
1916 integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
1917
1918acorn@^6.4.1: 2014acorn@^6.4.1:
1919 version "6.4.2" 2015 version "6.4.2"
1920 resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" 2016 resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
@@ -1989,11 +2085,6 @@ ajv-errors@^1.0.0:
1989 resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" 2085 resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
1990 integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== 2086 integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
1991 2087
1992ajv-keywords@^1.0.0:
1993 version "1.5.1"
1994 resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
1995 integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw=
1996
1997ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: 2088ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
1998 version "3.5.2" 2089 version "3.5.2"
1999 resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" 2090 resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
@@ -2009,13 +2100,15 @@ ajv@6.12.6, ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
2009 json-schema-traverse "^0.4.1" 2100 json-schema-traverse "^0.4.1"
2010 uri-js "^4.2.2" 2101 uri-js "^4.2.2"
2011 2102
2012ajv@^4.7.0: 2103ajv@^8.0.1:
2013 version "4.11.8" 2104 version "8.2.0"
2014 resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" 2105 resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.2.0.tgz#c89d3380a784ce81b2085f48811c4c101df4c602"
2015 integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= 2106 integrity sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==
2016 dependencies: 2107 dependencies:
2017 co "^4.6.0" 2108 fast-deep-equal "^3.1.1"
2018 json-stable-stringify "^1.0.1" 2109 json-schema-traverse "^1.0.0"
2110 require-from-string "^2.0.2"
2111 uri-js "^4.2.2"
2019 2112
2020alphanum-sort@^1.0.0: 2113alphanum-sort@^1.0.0:
2021 version "1.0.2" 2114 version "1.0.2"
@@ -2048,11 +2141,6 @@ ansi-colors@^3.0.0:
2048 resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" 2141 resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
2049 integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== 2142 integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
2050 2143
2051ansi-escapes@^1.1.0:
2052 version "1.4.0"
2053 resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
2054 integrity sha1-06ioOzGapneTZisT52HHkRQiMG4=
2055
2056ansi-escapes@^4.2.1: 2144ansi-escapes@^4.2.1:
2057 version "4.3.2" 2145 version "4.3.2"
2058 resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" 2146 resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
@@ -2210,7 +2298,7 @@ array-unique@^0.3.2:
2210 resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" 2298 resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
2211 integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= 2299 integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
2212 2300
2213arrify@^1.0.0: 2301arrify@^1.0.0, arrify@^1.0.1:
2214 version "1.0.1" 2302 version "1.0.1"
2215 resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 2303 resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
2216 integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= 2304 integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
@@ -2255,6 +2343,11 @@ ast-types-flow@0.0.7:
2255 resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" 2343 resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
2256 integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= 2344 integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
2257 2345
2346astral-regex@^2.0.0:
2347 version "2.0.0"
2348 resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
2349 integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
2350
2258async-each@^1.0.1: 2351async-each@^1.0.1:
2259 version "1.0.3" 2352 version "1.0.3"
2260 resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" 2353 resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
@@ -2299,6 +2392,19 @@ autoprefixer@10.2.4:
2299 normalize-range "^0.1.2" 2392 normalize-range "^0.1.2"
2300 postcss-value-parser "^4.1.0" 2393 postcss-value-parser "^4.1.0"
2301 2394
2395autoprefixer@^9.8.6:
2396 version "9.8.6"
2397 resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
2398 integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==
2399 dependencies:
2400 browserslist "^4.12.0"
2401 caniuse-lite "^1.0.30001109"
2402 colorette "^1.2.1"
2403 normalize-range "^0.1.2"
2404 num2fraction "^1.2.2"
2405 postcss "^7.0.32"
2406 postcss-value-parser "^4.1.0"
2407
2302aws-sign2@~0.7.0: 2408aws-sign2@~0.7.0:
2303 version "0.7.0" 2409 version "0.7.0"
2304 resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" 2410 resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@@ -2338,11 +2444,21 @@ backo2@~1.0.2:
2338 resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" 2444 resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
2339 integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= 2445 integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
2340 2446
2447bail@^1.0.0:
2448 version "1.0.5"
2449 resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776"
2450 integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
2451
2341balanced-match@^1.0.0: 2452balanced-match@^1.0.0:
2342 version "1.0.2" 2453 version "1.0.2"
2343 resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 2454 resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
2344 integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 2455 integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
2345 2456
2457balanced-match@^2.0.0:
2458 version "2.0.0"
2459 resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
2460 integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
2461
2346base64-arraybuffer@0.1.4: 2462base64-arraybuffer@0.1.4:
2347 version "0.1.4" 2463 version "0.1.4"
2348 resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" 2464 resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
@@ -2692,6 +2808,17 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4
2692 escalade "^3.1.1" 2808 escalade "^3.1.1"
2693 node-releases "^1.1.71" 2809 node-releases "^1.1.71"
2694 2810
2811browserslist@^4.12.0:
2812 version "4.16.5"
2813 resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.5.tgz#952825440bca8913c62d0021334cbe928ef062ae"
2814 integrity sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A==
2815 dependencies:
2816 caniuse-lite "^1.0.30001214"
2817 colorette "^1.2.2"
2818 electron-to-chromium "^1.3.719"
2819 escalade "^3.1.1"
2820 node-releases "^1.1.71"
2821
2695browserstack@^1.5.1: 2822browserstack@^1.5.1:
2696 version "1.6.1" 2823 version "1.6.1"
2697 resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.6.1.tgz#e051f9733ec3b507659f395c7a4765a1b1e358b3" 2824 resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.6.1.tgz#e051f9733ec3b507659f395c7a4765a1b1e358b3"
@@ -2893,13 +3020,6 @@ caller-callsite@^2.0.0:
2893 dependencies: 3020 dependencies:
2894 callsites "^2.0.0" 3021 callsites "^2.0.0"
2895 3022
2896caller-path@^0.1.0:
2897 version "0.1.0"
2898 resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
2899 integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=
2900 dependencies:
2901 callsites "^0.2.0"
2902
2903caller-path@^2.0.0: 3023caller-path@^2.0.0:
2904 version "2.0.0" 3024 version "2.0.0"
2905 resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" 3025 resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
@@ -2907,11 +3027,6 @@ caller-path@^2.0.0:
2907 dependencies: 3027 dependencies:
2908 caller-callsite "^2.0.0" 3028 caller-callsite "^2.0.0"
2909 3029
2910callsites@^0.2.0:
2911 version "0.2.0"
2912 resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
2913 integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=
2914
2915callsites@^2.0.0: 3030callsites@^2.0.0:
2916 version "2.0.0" 3031 version "2.0.0"
2917 resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" 3032 resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
@@ -2930,7 +3045,16 @@ camel-case@^4.1.1:
2930 pascal-case "^3.1.2" 3045 pascal-case "^3.1.2"
2931 tslib "^2.0.3" 3046 tslib "^2.0.3"
2932 3047
2933camelcase@5.3.1, camelcase@^5.0.0: 3048camelcase-keys@^6.2.2:
3049 version "6.2.2"
3050 resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0"
3051 integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==
3052 dependencies:
3053 camelcase "^5.3.1"
3054 map-obj "^4.0.0"
3055 quick-lru "^4.0.1"
3056
3057camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1:
2934 version "5.3.1" 3058 version "5.3.1"
2935 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 3059 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
2936 integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 3060 integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
@@ -2960,6 +3084,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001181, can
2960 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9" 3084 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9"
2961 integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA== 3085 integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==
2962 3086
3087caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001214:
3088 version "1.0.30001219"
3089 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001219.tgz#5bfa5d0519f41f993618bd318f606a4c4c16156b"
3090 integrity sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==
3091
2963canonical-path@1.0.0: 3092canonical-path@1.0.0:
2964 version "1.0.0" 3093 version "1.0.0"
2965 resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" 3094 resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d"
@@ -2977,7 +3106,7 @@ caseless@~0.12.0:
2977 dependencies: 3106 dependencies:
2978 traverse ">=0.3.0 <0.4" 3107 traverse ">=0.3.0 <0.4"
2979 3108
2980chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: 3109chalk@^1.1.1, chalk@^1.1.3:
2981 version "1.1.3" 3110 version "1.1.3"
2982 resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 3111 resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
2983 integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= 3112 integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@@ -3005,6 +3134,21 @@ chalk@^4.1.0:
3005 ansi-styles "^4.1.0" 3134 ansi-styles "^4.1.0"
3006 supports-color "^7.1.0" 3135 supports-color "^7.1.0"
3007 3136
3137character-entities-legacy@^1.0.0:
3138 version "1.1.4"
3139 resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1"
3140 integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==
3141
3142character-entities@^1.0.0:
3143 version "1.2.4"
3144 resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b"
3145 integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==
3146
3147character-reference-invalid@^1.0.0:
3148 version "1.1.4"
3149 resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
3150 integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
3151
3008chardet@^0.7.0: 3152chardet@^0.7.0:
3009 version "0.7.0" 3153 version "0.7.0"
3010 resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 3154 resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -3125,11 +3269,6 @@ circular-dependency-plugin@5.2.2:
3125 resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600" 3269 resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600"
3126 integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ== 3270 integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==
3127 3271
3128circular-json@^0.3.1:
3129 version "0.3.3"
3130 resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
3131 integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==
3132
3133class-utils@^0.3.5: 3272class-utils@^0.3.5:
3134 version "0.3.6" 3273 version "0.3.6"
3135 resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" 3274 resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -3152,13 +3291,6 @@ clean-stack@^2.0.0:
3152 resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" 3291 resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
3153 integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== 3292 integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
3154 3293
3155cli-cursor@^1.0.1:
3156 version "1.0.2"
3157 resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
3158 integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=
3159 dependencies:
3160 restore-cursor "^1.0.1"
3161
3162cli-cursor@^3.1.0: 3294cli-cursor@^3.1.0:
3163 version "3.1.0" 3295 version "3.1.0"
3164 resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" 3296 resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
@@ -3171,11 +3303,6 @@ cli-spinners@^2.5.0:
3171 resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" 3303 resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939"
3172 integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== 3304 integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==
3173 3305
3174cli-width@^2.0.0:
3175 version "2.2.1"
3176 resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
3177 integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
3178
3179cli-width@^3.0.0: 3306cli-width@^3.0.0:
3180 version "3.0.0" 3307 version "3.0.0"
3181 resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" 3308 resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
@@ -3226,16 +3353,18 @@ clone-deep@^4.0.1:
3226 kind-of "^6.0.2" 3353 kind-of "^6.0.2"
3227 shallow-clone "^3.0.0" 3354 shallow-clone "^3.0.0"
3228 3355
3356clone-regexp@^2.1.0:
3357 version "2.2.0"
3358 resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f"
3359 integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==
3360 dependencies:
3361 is-regexp "^2.0.0"
3362
3229clone@^1.0.2: 3363clone@^1.0.2:
3230 version "1.0.4" 3364 version "1.0.4"
3231 resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" 3365 resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
3232 integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= 3366 integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
3233 3367
3234co@^4.6.0:
3235 version "4.6.0"
3236 resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
3237 integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
3238
3239coa@^2.0.2: 3368coa@^2.0.2:
3240 version "2.0.2" 3369 version "2.0.2"
3241 resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" 3370 resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
@@ -3335,7 +3464,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
3335 dependencies: 3464 dependencies:
3336 delayed-stream "~1.0.0" 3465 delayed-stream "~1.0.0"
3337 3466
3338commander@^2.11.0, commander@^2.12.1, commander@^2.20.0, commander@^2.8.1: 3467commander@^2.11.0, commander@^2.12.1, commander@^2.20.0:
3339 version "2.20.3" 3468 version "2.20.3"
3340 resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 3469 resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
3341 integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 3470 integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -3404,7 +3533,7 @@ concat-map@0.0.1:
3404 resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 3533 resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
3405 integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 3534 integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
3406 3535
3407concat-stream@^1.4.6, concat-stream@^1.5.0: 3536concat-stream@^1.5.0:
3408 version "1.6.2" 3537 version "1.6.2"
3409 resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 3538 resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
3410 integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 3539 integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
@@ -3939,7 +4068,7 @@ date-format@^3.0.0:
3939 resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" 4068 resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95"
3940 integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== 4069 integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==
3941 4070
3942debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3: 4071debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
3943 version "2.6.9" 4072 version "2.6.9"
3944 resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 4073 resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
3945 integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 4074 integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -3967,7 +4096,15 @@ debug@~3.1.0:
3967 dependencies: 4096 dependencies:
3968 ms "2.0.0" 4097 ms "2.0.0"
3969 4098
3970decamelize@^1.1.1, decamelize@^1.2.0: 4099decamelize-keys@^1.1.0:
4100 version "1.1.0"
4101 resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
4102 integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=
4103 dependencies:
4104 decamelize "^1.1.0"
4105 map-obj "^1.0.0"
4106
4107decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0:
3971 version "1.2.0" 4108 version "1.2.0"
3972 resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 4109 resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
3973 integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 4110 integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -3996,11 +4133,6 @@ deep-equal@^1.0.1:
3996 object-keys "^1.1.1" 4133 object-keys "^1.1.1"
3997 regexp.prototype.flags "^1.2.0" 4134 regexp.prototype.flags "^1.2.0"
3998 4135
3999deep-is@~0.1.3:
4000 version "0.1.3"
4001 resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
4002 integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
4003
4004deepmerge@^4.2.2: 4136deepmerge@^4.2.2:
4005 version "4.2.2" 4137 version "4.2.2"
4006 resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" 4138 resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@@ -4178,14 +4310,6 @@ doctrine@0.7.2:
4178 esutils "^1.1.6" 4310 esutils "^1.1.6"
4179 isarray "0.0.1" 4311 isarray "0.0.1"
4180 4312
4181doctrine@^1.2.2:
4182 version "1.5.0"
4183 resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
4184 integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
4185 dependencies:
4186 esutils "^2.0.2"
4187 isarray "^1.0.0"
4188
4189dom-converter@^0.2: 4313dom-converter@^0.2:
4190 version "0.2.0" 4314 version "0.2.0"
4191 resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" 4315 resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@@ -4326,6 +4450,11 @@ electron-to-chromium@^1.3.712:
4326 resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz#ae467ffe5f95961c6d41ceefe858fc36eb53b38f" 4450 resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz#ae467ffe5f95961c6d41ceefe858fc36eb53b38f"
4327 integrity sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw== 4451 integrity sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==
4328 4452
4453electron-to-chromium@^1.3.719:
4454 version "1.3.723"
4455 resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.723.tgz#52769a75635342a4db29af5f1e40bd3dad02c877"
4456 integrity sha512-L+WXyXI7c7+G1V8ANzRsPI5giiimLAUDC6Zs1ojHHPhYXb3k/iTABFmWjivEtsWrRQymjnO66/rO2ZTABGdmWg==
4457
4329elliptic@^6.5.3: 4458elliptic@^6.5.3:
4330 version "6.5.4" 4459 version "6.5.4"
4331 resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" 4460 resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
@@ -4522,7 +4651,7 @@ es-to-primitive@^1.2.1:
4522 is-date-object "^1.0.1" 4651 is-date-object "^1.0.1"
4523 is-symbol "^1.0.2" 4652 is-symbol "^1.0.2"
4524 4653
4525es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14: 4654es5-ext@^0.10.35, es5-ext@^0.10.50:
4526 version "0.10.53" 4655 version "0.10.53"
4527 resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" 4656 resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1"
4528 integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== 4657 integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==
@@ -4531,7 +4660,7 @@ es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14:
4531 es6-symbol "~3.1.3" 4660 es6-symbol "~3.1.3"
4532 next-tick "~1.0.0" 4661 next-tick "~1.0.0"
4533 4662
4534es6-iterator@2.0.3, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: 4663es6-iterator@2.0.3, es6-iterator@~2.0.3:
4535 version "2.0.3" 4664 version "2.0.3"
4536 resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" 4665 resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
4537 integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= 4666 integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
@@ -4540,18 +4669,6 @@ es6-iterator@2.0.3, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.
4540 es5-ext "^0.10.35" 4669 es5-ext "^0.10.35"
4541 es6-symbol "^3.1.1" 4670 es6-symbol "^3.1.1"
4542 4671
4543es6-map@^0.1.3:
4544 version "0.1.5"
4545 resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
4546 integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=
4547 dependencies:
4548 d "1"
4549 es5-ext "~0.10.14"
4550 es6-iterator "~2.0.1"
4551 es6-set "~0.1.5"
4552 es6-symbol "~3.1.1"
4553 event-emitter "~0.3.5"
4554
4555es6-promise@^4.0.3: 4672es6-promise@^4.0.3:
4556 version "4.2.8" 4673 version "4.2.8"
4557 resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" 4674 resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
@@ -4564,26 +4681,7 @@ es6-promisify@^5.0.0:
4564 dependencies: 4681 dependencies:
4565 es6-promise "^4.0.3" 4682 es6-promise "^4.0.3"
4566 4683
4567es6-set@~0.1.5: 4684es6-symbol@^3.1.1, es6-symbol@~3.1.3:
4568 version "0.1.5"
4569 resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
4570 integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=
4571 dependencies:
4572 d "1"
4573 es5-ext "~0.10.14"
4574 es6-iterator "~2.0.1"
4575 es6-symbol "3.1.1"
4576 event-emitter "~0.3.5"
4577
4578es6-symbol@3.1.1:
4579 version "3.1.1"
4580 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
4581 integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
4582 dependencies:
4583 d "1"
4584 es5-ext "~0.10.14"
4585
4586es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3:
4587 version "3.1.3" 4685 version "3.1.3"
4588 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" 4686 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
4589 integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== 4687 integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
@@ -4591,16 +4689,6 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3:
4591 d "^1.0.1" 4689 d "^1.0.1"
4592 ext "^1.1.2" 4690 ext "^1.1.2"
4593 4691
4594es6-weak-map@^2.0.1:
4595 version "2.0.3"
4596 resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53"
4597 integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==
4598 dependencies:
4599 d "1"
4600 es5-ext "^0.10.46"
4601 es6-iterator "^2.0.3"
4602 es6-symbol "^3.1.1"
4603
4604escalade@^3.1.1: 4692escalade@^3.1.1:
4605 version "3.1.1" 4693 version "3.1.1"
4606 resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 4694 resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -4621,16 +4709,6 @@ escape-string-regexp@^4.0.0:
4621 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 4709 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
4622 integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 4710 integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
4623 4711
4624escope@^3.6.0:
4625 version "3.6.0"
4626 resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
4627 integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=
4628 dependencies:
4629 es6-map "^0.1.3"
4630 es6-weak-map "^2.0.1"
4631 esrecurse "^4.1.0"
4632 estraverse "^4.1.1"
4633
4634eslint-scope@^4.0.3: 4712eslint-scope@^4.0.3:
4635 version "4.0.3" 4713 version "4.0.3"
4636 resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" 4714 resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
@@ -4639,53 +4717,6 @@ eslint-scope@^4.0.3:
4639 esrecurse "^4.1.0" 4717 esrecurse "^4.1.0"
4640 estraverse "^4.1.1" 4718 estraverse "^4.1.1"
4641 4719
4642eslint@^2.7.0:
4643 version "2.13.1"
4644 resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.13.1.tgz#e4cc8fa0f009fb829aaae23855a29360be1f6c11"
4645 integrity sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE=
4646 dependencies:
4647 chalk "^1.1.3"
4648 concat-stream "^1.4.6"
4649 debug "^2.1.1"
4650 doctrine "^1.2.2"
4651 es6-map "^0.1.3"
4652 escope "^3.6.0"
4653 espree "^3.1.6"
4654 estraverse "^4.2.0"
4655 esutils "^2.0.2"
4656 file-entry-cache "^1.1.1"
4657 glob "^7.0.3"
4658 globals "^9.2.0"
4659 ignore "^3.1.2"
4660 imurmurhash "^0.1.4"
4661 inquirer "^0.12.0"
4662 is-my-json-valid "^2.10.0"
4663 is-resolvable "^1.0.0"
4664 js-yaml "^3.5.1"
4665 json-stable-stringify "^1.0.0"
4666 levn "^0.3.0"
4667 lodash "^4.0.0"
4668 mkdirp "^0.5.0"
4669 optionator "^0.8.1"
4670 path-is-absolute "^1.0.0"
4671 path-is-inside "^1.0.1"
4672 pluralize "^1.2.1"
4673 progress "^1.1.8"
4674 require-uncached "^1.0.2"
4675 shelljs "^0.6.0"
4676 strip-json-comments "~1.0.1"
4677 table "^3.7.8"
4678 text-table "~0.2.0"
4679 user-home "^2.0.0"
4680
4681espree@^3.1.6:
4682 version "3.5.4"
4683 resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
4684 integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==
4685 dependencies:
4686 acorn "^5.5.0"
4687 acorn-jsx "^3.0.0"
4688
4689esprima@^4.0.0: 4720esprima@^4.0.0:
4690 version "4.0.1" 4721 version "4.0.1"
4691 resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 4722 resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
@@ -4698,7 +4729,7 @@ esrecurse@^4.1.0:
4698 dependencies: 4729 dependencies:
4699 estraverse "^5.2.0" 4730 estraverse "^5.2.0"
4700 4731
4701estraverse@^4.1.1, estraverse@^4.2.0: 4732estraverse@^4.1.1:
4702 version "4.3.0" 4733 version "4.3.0"
4703 resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 4734 resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
4704 integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 4735 integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
@@ -4728,14 +4759,6 @@ etag@~1.8.1:
4728 resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 4759 resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
4729 integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 4760 integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
4730 4761
4731event-emitter@~0.3.5:
4732 version "0.3.5"
4733 resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
4734 integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=
4735 dependencies:
4736 d "1"
4737 es5-ext "~0.10.14"
4738
4739eventemitter3@^4.0.0, eventemitter3@^4.0.3: 4762eventemitter3@^4.0.0, eventemitter3@^4.0.3:
4740 version "4.0.7" 4763 version "4.0.7"
4741 resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" 4764 resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
@@ -4802,10 +4825,12 @@ execa@^5.0.0:
4802 signal-exit "^3.0.3" 4825 signal-exit "^3.0.3"
4803 strip-final-newline "^2.0.0" 4826 strip-final-newline "^2.0.0"
4804 4827
4805exit-hook@^1.0.0: 4828execall@^2.0.0:
4806 version "1.1.1" 4829 version "2.0.0"
4807 resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" 4830 resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45"
4808 integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= 4831 integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==
4832 dependencies:
4833 clone-regexp "^2.1.0"
4809 4834
4810exit@^0.1.2: 4835exit@^0.1.2:
4811 version "0.1.2" 4836 version "0.1.2"
@@ -4926,7 +4951,7 @@ fast-deep-equal@^3.1.1:
4926 resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 4951 resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
4927 integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 4952 integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
4928 4953
4929fast-glob@^3.1.1, fast-glob@^3.2.4: 4954fast-glob@^3.1.1, fast-glob@^3.2.4, fast-glob@^3.2.5:
4930 version "3.2.5" 4955 version "3.2.5"
4931 resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" 4956 resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
4932 integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== 4957 integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
@@ -4943,11 +4968,6 @@ fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@^2.0.0:
4943 resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 4968 resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
4944 integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 4969 integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
4945 4970
4946fast-levenshtein@~2.0.6:
4947 version "2.0.6"
4948 resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
4949 integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
4950
4951fastest-levenshtein@^1.0.12: 4971fastest-levenshtein@^1.0.12:
4952 version "1.0.12" 4972 version "1.0.12"
4953 resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" 4973 resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
@@ -4977,14 +4997,6 @@ figgy-pudding@^3.5.1:
4977 resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" 4997 resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
4978 integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== 4998 integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
4979 4999
4980figures@^1.3.5:
4981 version "1.7.0"
4982 resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
4983 integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=
4984 dependencies:
4985 escape-string-regexp "^1.0.5"
4986 object-assign "^4.1.0"
4987
4988figures@^3.0.0: 5000figures@^3.0.0:
4989 version "3.2.0" 5001 version "3.2.0"
4990 resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" 5002 resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
@@ -4992,13 +5004,12 @@ figures@^3.0.0:
4992 dependencies: 5004 dependencies:
4993 escape-string-regexp "^1.0.5" 5005 escape-string-regexp "^1.0.5"
4994 5006
4995file-entry-cache@^1.1.1: 5007file-entry-cache@^6.0.1:
4996 version "1.3.1" 5008 version "6.0.1"
4997 resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-1.3.1.tgz#44c61ea607ae4be9c1402f41f44270cbfe334ff8" 5009 resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
4998 integrity sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g= 5010 integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
4999 dependencies: 5011 dependencies:
5000 flat-cache "^1.2.1" 5012 flat-cache "^3.0.4"
5001 object-assign "^4.0.1"
5002 5013
5003file-loader@6.2.0, file-loader@^6.0.0: 5014file-loader@6.2.0, file-loader@^6.0.0:
5004 version "6.2.0" 5015 version "6.2.0"
@@ -5091,21 +5102,24 @@ find-up@^4.0.0, find-up@^4.1.0:
5091 locate-path "^5.0.0" 5102 locate-path "^5.0.0"
5092 path-exists "^4.0.0" 5103 path-exists "^4.0.0"
5093 5104
5094flat-cache@^1.2.1: 5105flat-cache@^3.0.4:
5095 version "1.3.4" 5106 version "3.0.4"
5096 resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" 5107 resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
5097 integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== 5108 integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
5098 dependencies: 5109 dependencies:
5099 circular-json "^0.3.1" 5110 flatted "^3.1.0"
5100 graceful-fs "^4.1.2" 5111 rimraf "^3.0.2"
5101 rimraf "~2.6.2"
5102 write "^0.2.1"
5103 5112
5104flatted@^2.0.1: 5113flatted@^2.0.1:
5105 version "2.0.2" 5114 version "2.0.2"
5106 resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" 5115 resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
5107 integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== 5116 integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
5108 5117
5118flatted@^3.1.0:
5119 version "3.1.1"
5120 resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
5121 integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
5122
5109flush-write-stream@^1.0.0: 5123flush-write-stream@^1.0.0:
5110 version "1.1.1" 5124 version "1.1.1"
5111 resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" 5125 resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@@ -5178,13 +5192,6 @@ from2@^2.1.0:
5178 inherits "^2.0.1" 5192 inherits "^2.0.1"
5179 readable-stream "^2.0.0" 5193 readable-stream "^2.0.0"
5180 5194
5181front-matter@2.1.2:
5182 version "2.1.2"
5183 resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-2.1.2.tgz#f75983b9f2f413be658c93dfd7bd8ce4078f5cdb"
5184 integrity sha1-91mDufL0E75ljJPf172M5AePXNs=
5185 dependencies:
5186 js-yaml "^3.4.6"
5187
5188fs-chunk-store@^2.0.2: 5195fs-chunk-store@^2.0.2:
5189 version "2.0.3" 5196 version "2.0.3"
5190 resolved "https://registry.yarnpkg.com/fs-chunk-store/-/fs-chunk-store-2.0.3.tgz#21e51f1833a84a07cb5e911d058dae084030375a" 5197 resolved "https://registry.yarnpkg.com/fs-chunk-store/-/fs-chunk-store-2.0.3.tgz#21e51f1833a84a07cb5e911d058dae084030375a"
@@ -5206,15 +5213,6 @@ fs-extra@4.0.2:
5206 jsonfile "^4.0.0" 5213 jsonfile "^4.0.0"
5207 universalify "^0.1.0" 5214 universalify "^0.1.0"
5208 5215
5209fs-extra@^3.0.1:
5210 version "3.0.1"
5211 resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
5212 integrity sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=
5213 dependencies:
5214 graceful-fs "^4.1.2"
5215 jsonfile "^3.0.0"
5216 universalify "^0.1.0"
5217
5218fs-extra@^8.1.0: 5216fs-extra@^8.1.0:
5219 version "8.1.0" 5217 version "8.1.0"
5220 resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" 5218 resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
@@ -5278,20 +5276,6 @@ gauge@~2.7.3:
5278 strip-ansi "^3.0.1" 5276 strip-ansi "^3.0.1"
5279 wide-align "^1.1.0" 5277 wide-align "^1.1.0"
5280 5278
5281generate-function@^2.0.0:
5282 version "2.3.1"
5283 resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
5284 integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
5285 dependencies:
5286 is-property "^1.0.2"
5287
5288generate-object-property@^1.1.0:
5289 version "1.2.0"
5290 resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
5291 integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=
5292 dependencies:
5293 is-property "^1.0.0"
5294
5295gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: 5279gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2:
5296 version "1.0.0-beta.2" 5280 version "1.0.0-beta.2"
5297 resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" 5281 resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -5382,7 +5366,7 @@ glob@7.1.2:
5382 once "^1.3.0" 5366 once "^1.3.0"
5383 path-is-absolute "^1.0.0" 5367 path-is-absolute "^1.0.0"
5384 5368
5385glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: 5369glob@7.1.6, glob@^7.0.3, glob@^7.0.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
5386 version "7.1.6" 5370 version "7.1.6"
5387 resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 5371 resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
5388 integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 5372 integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -5394,6 +5378,22 @@ glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.6, glob@^7.1.1, glob@^7.1.3, glo
5394 once "^1.3.0" 5378 once "^1.3.0"
5395 path-is-absolute "^1.0.0" 5379 path-is-absolute "^1.0.0"
5396 5380
5381global-modules@^2.0.0:
5382 version "2.0.0"
5383 resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
5384 integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
5385 dependencies:
5386 global-prefix "^3.0.0"
5387
5388global-prefix@^3.0.0:
5389 version "3.0.0"
5390 resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
5391 integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
5392 dependencies:
5393 ini "^1.3.5"
5394 kind-of "^6.0.2"
5395 which "^1.3.1"
5396
5397global@4.3.2: 5397global@4.3.2:
5398 version "4.3.2" 5398 version "4.3.2"
5399 resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" 5399 resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
@@ -5415,12 +5415,7 @@ globals@^11.1.0:
5415 resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 5415 resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
5416 integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 5416 integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
5417 5417
5418globals@^9.2.0: 5418globby@^11.0.1, globby@^11.0.3:
5419 version "9.18.0"
5420 resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
5421 integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
5422
5423globby@^11.0.1:
5424 version "11.0.3" 5419 version "11.0.3"
5425 resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" 5420 resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb"
5426 integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== 5421 integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==
@@ -5455,21 +5450,17 @@ globby@^6.1.0:
5455 pify "^2.0.0" 5450 pify "^2.0.0"
5456 pinkie-promise "^2.0.0" 5451 pinkie-promise "^2.0.0"
5457 5452
5458globule@^1.0.0: 5453globjoin@^0.1.4:
5459 version "1.3.2" 5454 version "0.1.4"
5460 resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" 5455 resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
5461 integrity sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA== 5456 integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
5462 dependencies:
5463 glob "~7.1.1"
5464 lodash "~4.17.10"
5465 minimatch "~3.0.2"
5466 5457
5467gonzales-pe-sl@^4.2.3: 5458gonzales-pe@^4.3.0:
5468 version "4.2.3" 5459 version "4.3.0"
5469 resolved "https://registry.yarnpkg.com/gonzales-pe-sl/-/gonzales-pe-sl-4.2.3.tgz#6a868bc380645f141feeb042c6f97fcc71b59fe6" 5460 resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
5470 integrity sha1-aoaLw4BkXxQf7rBCxvl/zHG1n+Y= 5461 integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==
5471 dependencies: 5462 dependencies:
5472 minimist "1.1.x" 5463 minimist "^1.2.5"
5473 5464
5474graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4: 5465graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4:
5475 version "4.2.6" 5466 version "4.2.6"
@@ -5501,6 +5492,11 @@ har-validator@~5.1.3:
5501 ajv "^6.12.3" 5492 ajv "^6.12.3"
5502 har-schema "^2.0.0" 5493 har-schema "^2.0.0"
5503 5494
5495hard-rejection@^2.1.0:
5496 version "2.1.0"
5497 resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
5498 integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
5499
5504has-ansi@^2.0.0: 5500has-ansi@^2.0.0:
5505 version "2.0.0" 5501 version "2.0.0"
5506 resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 5502 resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -5699,6 +5695,11 @@ html-minifier-terser@^5.0.1, html-minifier-terser@^5.1.1:
5699 relateurl "^0.2.7" 5695 relateurl "^0.2.7"
5700 terser "^4.6.3" 5696 terser "^4.6.3"
5701 5697
5698html-tags@^3.1.0:
5699 version "3.1.0"
5700 resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140"
5701 integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==
5702
5702html-webpack-plugin@^4.0.3: 5703html-webpack-plugin@^4.0.3:
5703 version "4.5.2" 5704 version "4.5.2"
5704 resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" 5705 resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12"
@@ -5714,7 +5715,7 @@ html-webpack-plugin@^4.0.3:
5714 tapable "^1.1.3" 5715 tapable "^1.1.3"
5715 util.promisify "1.0.0" 5716 util.promisify "1.0.0"
5716 5717
5717htmlparser2@^3.10.1: 5718htmlparser2@^3.10.0, htmlparser2@^3.10.1:
5718 version "3.10.1" 5719 version "3.10.1"
5719 resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" 5720 resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
5720 integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== 5721 integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@@ -5912,12 +5913,7 @@ ignore-walk@^3.0.3:
5912 dependencies: 5913 dependencies:
5913 minimatch "^3.0.4" 5914 minimatch "^3.0.4"
5914 5915
5915ignore@^3.1.2: 5916ignore@^5.1.4, ignore@^5.1.8:
5916 version "3.3.10"
5917 resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
5918 integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
5919
5920ignore@^5.1.4:
5921 version "5.1.8" 5917 version "5.1.8"
5922 resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" 5918 resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
5923 integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== 5919 integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
@@ -5955,6 +5951,11 @@ import-fresh@^3.2.1:
5955 parent-module "^1.0.0" 5951 parent-module "^1.0.0"
5956 resolve-from "^4.0.0" 5952 resolve-from "^4.0.0"
5957 5953
5954import-lazy@^4.0.0:
5955 version "4.0.0"
5956 resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
5957 integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
5958
5958import-local@^2.0.0: 5959import-local@^2.0.0:
5959 version "2.0.0" 5960 version "2.0.0"
5960 resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" 5961 resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
@@ -6024,7 +6025,7 @@ ini@2.0.0:
6024 resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" 6025 resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
6025 integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== 6026 integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
6026 6027
6027ini@^1.3.4: 6028ini@^1.3.4, ini@^1.3.5:
6028 version "1.3.8" 6029 version "1.3.8"
6029 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" 6030 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
6030 integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== 6031 integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
@@ -6048,25 +6049,6 @@ inquirer@7.3.3:
6048 strip-ansi "^6.0.0" 6049 strip-ansi "^6.0.0"
6049 through "^2.3.6" 6050 through "^2.3.6"
6050 6051
6051inquirer@^0.12.0:
6052 version "0.12.0"
6053 resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
6054 integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=
6055 dependencies:
6056 ansi-escapes "^1.1.0"
6057 ansi-regex "^2.0.0"
6058 chalk "^1.0.0"
6059 cli-cursor "^1.0.1"
6060 cli-width "^2.0.0"
6061 figures "^1.3.5"
6062 lodash "^4.3.0"
6063 readline2 "^1.0.1"
6064 run-async "^0.1.0"
6065 rx-lite "^3.1.2"
6066 string-width "^1.0.1"
6067 strip-ansi "^3.0.0"
6068 through "^2.3.6"
6069
6070internal-ip@^4.3.0: 6052internal-ip@^4.3.0:
6071 version "4.3.0" 6053 version "4.3.0"
6072 resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" 6054 resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
@@ -6136,6 +6118,19 @@ is-accessor-descriptor@^1.0.0:
6136 dependencies: 6118 dependencies:
6137 kind-of "^6.0.0" 6119 kind-of "^6.0.0"
6138 6120
6121is-alphabetical@^1.0.0:
6122 version "1.0.4"
6123 resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d"
6124 integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==
6125
6126is-alphanumerical@^1.0.0:
6127 version "1.0.4"
6128 resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf"
6129 integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==
6130 dependencies:
6131 is-alphabetical "^1.0.0"
6132 is-decimal "^1.0.0"
6133
6139is-arguments@^1.0.4: 6134is-arguments@^1.0.4:
6140 version "1.1.0" 6135 version "1.1.0"
6141 resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" 6136 resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"
@@ -6189,6 +6184,11 @@ is-buffer@^1.1.5:
6189 resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 6184 resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
6190 integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 6185 integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
6191 6186
6187is-buffer@^2.0.0:
6188 version "2.0.5"
6189 resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
6190 integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
6191
6192is-callable@^1.1.4, is-callable@^1.2.3: 6192is-callable@^1.1.4, is-callable@^1.2.3:
6193 version "1.2.3" 6193 version "1.2.3"
6194 resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" 6194 resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
@@ -6232,6 +6232,11 @@ is-date-object@^1.0.1:
6232 resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" 6232 resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
6233 integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== 6233 integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
6234 6234
6235is-decimal@^1.0.0:
6236 version "1.0.4"
6237 resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5"
6238 integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==
6239
6235is-descriptor@^0.1.0: 6240is-descriptor@^0.1.0:
6236 version "0.1.6" 6241 version "0.1.6"
6237 resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" 6242 resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
@@ -6318,6 +6323,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
6318 dependencies: 6323 dependencies:
6319 is-extglob "^2.1.1" 6324 is-extglob "^2.1.1"
6320 6325
6326is-hexadecimal@^1.0.0:
6327 version "1.0.4"
6328 resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"
6329 integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==
6330
6321is-interactive@^1.0.0: 6331is-interactive@^1.0.0:
6322 version "1.0.0" 6332 version "1.0.0"
6323 resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" 6333 resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
@@ -6328,22 +6338,6 @@ is-lambda@^1.0.1:
6328 resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" 6338 resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
6329 integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= 6339 integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=
6330 6340
6331is-my-ip-valid@^1.0.0:
6332 version "1.0.0"
6333 resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824"
6334 integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==
6335
6336is-my-json-valid@^2.10.0:
6337 version "2.20.5"
6338 resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.5.tgz#5eca6a8232a687f68869b7361be1612e7512e5df"
6339 integrity sha512-VTPuvvGQtxvCeghwspQu1rBgjYUT6FGxPlvFKbYuFtgc4ADsX3U5ihZOYN0qyU6u+d4X9xXb0IT5O6QpXKt87A==
6340 dependencies:
6341 generate-function "^2.0.0"
6342 generate-object-property "^1.1.0"
6343 is-my-ip-valid "^1.0.0"
6344 jsonpointer "^4.0.0"
6345 xtend "^4.0.0"
6346
6347is-negative-zero@^2.0.1: 6341is-negative-zero@^2.0.1:
6348 version "2.0.1" 6342 version "2.0.1"
6349 resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" 6343 resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
@@ -6409,6 +6403,16 @@ is-path-inside@^2.1.0:
6409 dependencies: 6403 dependencies:
6410 path-is-inside "^1.0.2" 6404 path-is-inside "^1.0.2"
6411 6405
6406is-plain-obj@^1.1.0:
6407 version "1.1.0"
6408 resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
6409 integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
6410
6411is-plain-obj@^2.0.0:
6412 version "2.1.0"
6413 resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
6414 integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
6415
6412is-plain-object@^2.0.3, is-plain-object@^2.0.4: 6416is-plain-object@^2.0.3, is-plain-object@^2.0.4:
6413 version "2.0.4" 6417 version "2.0.4"
6414 resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 6418 resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -6421,11 +6425,6 @@ is-plain-object@^5.0.0:
6421 resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" 6425 resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
6422 integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== 6426 integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
6423 6427
6424is-property@^1.0.0, is-property@^1.0.2:
6425 version "1.0.2"
6426 resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
6427 integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=
6428
6429is-regex@^1.0.4, is-regex@^1.1.2: 6428is-regex@^1.0.4, is-regex@^1.1.2:
6430 version "1.1.2" 6429 version "1.1.2"
6431 resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" 6430 resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251"
@@ -6434,6 +6433,11 @@ is-regex@^1.0.4, is-regex@^1.1.2:
6434 call-bind "^1.0.2" 6433 call-bind "^1.0.2"
6435 has-symbols "^1.0.1" 6434 has-symbols "^1.0.1"
6436 6435
6436is-regexp@^2.0.0:
6437 version "2.1.0"
6438 resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d"
6439 integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==
6440
6437is-resolvable@^1.0.0: 6441is-resolvable@^1.0.0:
6438 version "1.1.0" 6442 version "1.1.0"
6439 resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" 6443 resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@@ -6628,7 +6632,7 @@ js-tokens@^4.0.0:
6628 resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 6632 resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
6629 integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 6633 integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
6630 6634
6631js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4: 6635js-yaml@^3.13.1:
6632 version "3.14.1" 6636 version "3.14.1"
6633 resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" 6637 resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
6634 integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== 6638 integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
@@ -6671,18 +6675,16 @@ json-schema-traverse@^0.4.1:
6671 resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 6675 resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
6672 integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 6676 integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
6673 6677
6678json-schema-traverse@^1.0.0:
6679 version "1.0.0"
6680 resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
6681 integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
6682
6674json-schema@0.2.3: 6683json-schema@0.2.3:
6675 version "0.2.3" 6684 version "0.2.3"
6676 resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" 6685 resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
6677 integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= 6686 integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
6678 6687
6679json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
6680 version "1.0.1"
6681 resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
6682 integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
6683 dependencies:
6684 jsonify "~0.0.0"
6685
6686json-stringify-safe@~5.0.1: 6688json-stringify-safe@~5.0.1:
6687 version "5.0.1" 6689 version "5.0.1"
6688 resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 6690 resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -6712,13 +6714,6 @@ jsonc-parser@3.0.0:
6712 resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" 6714 resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22"
6713 integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== 6715 integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==
6714 6716
6715jsonfile@^3.0.0:
6716 version "3.0.1"
6717 resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
6718 integrity sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=
6719 optionalDependencies:
6720 graceful-fs "^4.1.6"
6721
6722jsonfile@^4.0.0: 6717jsonfile@^4.0.0:
6723 version "4.0.0" 6718 version "4.0.0"
6724 resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 6719 resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -6726,21 +6721,11 @@ jsonfile@^4.0.0:
6726 optionalDependencies: 6721 optionalDependencies:
6727 graceful-fs "^4.1.6" 6722 graceful-fs "^4.1.6"
6728 6723
6729jsonify@~0.0.0:
6730 version "0.0.0"
6731 resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
6732 integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
6733
6734jsonparse@^1.3.1: 6724jsonparse@^1.3.1:
6735 version "1.3.1" 6725 version "1.3.1"
6736 resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" 6726 resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
6737 integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= 6727 integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
6738 6728
6739jsonpointer@^4.0.0:
6740 version "4.1.0"
6741 resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.1.0.tgz#501fb89986a2389765ba09e6053299ceb4f2c2cc"
6742 integrity sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==
6743
6744jsprim@^1.2.2: 6729jsprim@^1.2.2:
6745 version "1.4.1" 6730 version "1.4.1"
6746 resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" 6731 resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -6887,7 +6872,7 @@ kind-of@^5.0.0:
6887 resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" 6872 resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
6888 integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== 6873 integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
6889 6874
6890kind-of@^6.0.0, kind-of@^6.0.2: 6875kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3:
6891 version "6.0.3" 6876 version "6.0.3"
6892 resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" 6877 resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
6893 integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== 6878 integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
@@ -6897,10 +6882,10 @@ klona@^2.0.3, klona@^2.0.4:
6897 resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" 6882 resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
6898 integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== 6883 integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
6899 6884
6900known-css-properties@^0.3.0: 6885known-css-properties@^0.21.0:
6901 version "0.3.0" 6886 version "0.21.0"
6902 resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.3.0.tgz#a3d135bbfc60ee8c6eacf2f7e7e6f2d4755e49a4" 6887 resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.21.0.tgz#15fbd0bbb83447f3ce09d8af247ed47c68ede80d"
6903 integrity sha512-QMQcnKAiQccfQTqtBh/qwquGZ2XK/DXND1jrcN9M8gMMy99Gwla7GQjndVUsEqIaRyP6bsFRuhwRj5poafBGJQ== 6888 integrity sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==
6904 6889
6905last-one-wins@^1.0.4: 6890last-one-wins@^1.0.4:
6906 version "1.0.4" 6891 version "1.0.4"
@@ -6940,14 +6925,6 @@ less@4.1.1:
6940 needle "^2.5.2" 6925 needle "^2.5.2"
6941 source-map "~0.6.0" 6926 source-map "~0.6.0"
6942 6927
6943levn@^0.3.0, levn@~0.3.0:
6944 version "0.3.0"
6945 resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
6946 integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
6947 dependencies:
6948 prelude-ls "~1.1.2"
6949 type-check "~0.3.2"
6950
6951license-webpack-plugin@2.3.11: 6928license-webpack-plugin@2.3.11:
6952 version "2.3.11" 6929 version "2.3.11"
6953 resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.11.tgz#0d93188a31fce350a44c86212badbaf33dcd29d8" 6930 resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.11.tgz#0d93188a31fce350a44c86212badbaf33dcd29d8"
@@ -7061,32 +7038,37 @@ lodash-es@^4.17.4:
7061 resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" 7038 resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
7062 integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== 7039 integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
7063 7040
7064lodash.capitalize@^4.1.0: 7041lodash.clonedeep@^4.5.0:
7065 version "4.2.1" 7042 version "4.5.0"
7066 resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" 7043 resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
7067 integrity sha1-+CbJtOKoUR2E46yinbBeGk87cqk= 7044 integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
7068 7045
7069lodash.kebabcase@^4.0.0: 7046lodash.flatten@^4.4.0:
7070 version "4.1.1" 7047 version "4.4.0"
7071 resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" 7048 resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
7072 integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= 7049 integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
7073 7050
7074lodash.memoize@^4.1.2: 7051lodash.memoize@^4.1.2:
7075 version "4.1.2" 7052 version "4.1.2"
7076 resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" 7053 resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
7077 integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= 7054 integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
7078 7055
7056lodash.truncate@^4.4.2:
7057 version "4.4.2"
7058 resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
7059 integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
7060
7079lodash.uniq@^4.5.0: 7061lodash.uniq@^4.5.0:
7080 version "4.5.0" 7062 version "4.5.0"
7081 resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" 7063 resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
7082 integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= 7064 integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
7083 7065
7084lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.3.0, lodash@~4.17.10: 7066lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21:
7085 version "4.17.21" 7067 version "4.17.21"
7086 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 7068 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
7087 integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 7069 integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
7088 7070
7089log-symbols@^4.0.0: 7071log-symbols@^4.0.0, log-symbols@^4.1.0:
7090 version "4.1.0" 7072 version "4.1.0"
7091 resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 7073 resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
7092 integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== 7074 integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
@@ -7110,6 +7092,11 @@ loglevel@^1.6.8:
7110 resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" 7092 resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
7111 integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== 7093 integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
7112 7094
7095longest-streak@^2.0.0:
7096 version "2.0.4"
7097 resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"
7098 integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==
7099
7113lower-case@^2.0.2: 7100lower-case@^2.0.2:
7114 version "2.0.2" 7101 version "2.0.2"
7115 resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" 7102 resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
@@ -7220,6 +7207,16 @@ map-cache@^0.2.2:
7220 resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 7207 resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
7221 integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= 7208 integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
7222 7209
7210map-obj@^1.0.0:
7211 version "1.0.1"
7212 resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
7213 integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
7214
7215map-obj@^4.0.0:
7216 version "4.2.1"
7217 resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7"
7218 integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==
7219
7223map-visit@^1.0.0: 7220map-visit@^1.0.0:
7224 version "1.0.0" 7221 version "1.0.0"
7225 resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" 7222 resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -7238,6 +7235,11 @@ markdown-it@12.0.4:
7238 mdurl "^1.0.1" 7235 mdurl "^1.0.1"
7239 uc.micro "^1.0.5" 7236 uc.micro "^1.0.5"
7240 7237
7238mathml-tag-names@^2.1.3:
7239 version "2.1.3"
7240 resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
7241 integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
7242
7241md5.js@^1.3.4: 7243md5.js@^1.3.4:
7242 version "1.3.5" 7244 version "1.3.5"
7243 resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" 7245 resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -7247,6 +7249,34 @@ md5.js@^1.3.4:
7247 inherits "^2.0.1" 7249 inherits "^2.0.1"
7248 safe-buffer "^5.1.2" 7250 safe-buffer "^5.1.2"
7249 7251
7252mdast-util-from-markdown@^0.8.0:
7253 version "0.8.5"
7254 resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c"
7255 integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==
7256 dependencies:
7257 "@types/mdast" "^3.0.0"
7258 mdast-util-to-string "^2.0.0"
7259 micromark "~2.11.0"
7260 parse-entities "^2.0.0"
7261 unist-util-stringify-position "^2.0.0"
7262
7263mdast-util-to-markdown@^0.6.0:
7264 version "0.6.5"
7265 resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz#b33f67ca820d69e6cc527a93d4039249b504bebe"
7266 integrity sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==
7267 dependencies:
7268 "@types/unist" "^2.0.0"
7269 longest-streak "^2.0.0"
7270 mdast-util-to-string "^2.0.0"
7271 parse-entities "^2.0.0"
7272 repeat-string "^1.0.0"
7273 zwitch "^1.0.0"
7274
7275mdast-util-to-string@^2.0.0:
7276 version "2.0.0"
7277 resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b"
7278 integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==
7279
7250mdn-data@2.0.14: 7280mdn-data@2.0.14:
7251 version "2.0.14" 7281 version "2.0.14"
7252 resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" 7282 resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
@@ -7306,6 +7336,24 @@ memory-fs@^0.5.0:
7306 errno "^0.1.3" 7336 errno "^0.1.3"
7307 readable-stream "^2.0.1" 7337 readable-stream "^2.0.1"
7308 7338
7339meow@^9.0.0:
7340 version "9.0.0"
7341 resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364"
7342 integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==
7343 dependencies:
7344 "@types/minimist" "^1.2.0"
7345 camelcase-keys "^6.2.2"
7346 decamelize "^1.2.0"
7347 decamelize-keys "^1.1.0"
7348 hard-rejection "^2.1.0"
7349 minimist-options "4.1.0"
7350 normalize-package-data "^3.0.0"
7351 read-pkg-up "^7.0.1"
7352 redent "^3.0.0"
7353 trim-newlines "^3.0.0"
7354 type-fest "^0.18.0"
7355 yargs-parser "^20.2.3"
7356
7309merge-descriptors@1.0.1: 7357merge-descriptors@1.0.1:
7310 version "1.0.1" 7358 version "1.0.1"
7311 resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 7359 resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -7328,16 +7376,19 @@ merge2@^1.3.0:
7328 resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 7376 resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
7329 integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 7377 integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
7330 7378
7331merge@^1.2.0:
7332 version "1.2.1"
7333 resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
7334 integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==
7335
7336methods@~1.1.2: 7379methods@~1.1.2:
7337 version "1.1.2" 7380 version "1.1.2"
7338 resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 7381 resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
7339 integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 7382 integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
7340 7383
7384micromark@~2.11.0:
7385 version "2.11.4"
7386 resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a"
7387 integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==
7388 dependencies:
7389 debug "^4.0.0"
7390 parse-entities "^2.0.0"
7391
7341micromatch@^3.1.10, micromatch@^3.1.4: 7392micromatch@^3.1.10, micromatch@^3.1.4:
7342 version "3.1.10" 7393 version "3.1.10"
7343 resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" 7394 resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@@ -7357,7 +7408,7 @@ micromatch@^3.1.10, micromatch@^3.1.4:
7357 snapdragon "^0.8.1" 7408 snapdragon "^0.8.1"
7358 to-regex "^3.0.2" 7409 to-regex "^3.0.2"
7359 7410
7360micromatch@^4.0.0, micromatch@^4.0.2: 7411micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4:
7361 version "4.0.4" 7412 version "4.0.4"
7362 resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" 7413 resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
7363 integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== 7414 integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
@@ -7417,6 +7468,11 @@ min-document@^2.19.0:
7417 dependencies: 7468 dependencies:
7418 dom-walk "^0.1.0" 7469 dom-walk "^0.1.0"
7419 7470
7471min-indent@^1.0.0:
7472 version "1.0.1"
7473 resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
7474 integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
7475
7420mini-css-extract-plugin@1.3.5: 7476mini-css-extract-plugin@1.3.5:
7421 version "1.3.5" 7477 version "1.3.5"
7422 resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.5.tgz#252166e78879c106e0130f229d44e0cbdfcebed3" 7478 resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.5.tgz#252166e78879c106e0130f229d44e0cbdfcebed3"
@@ -7445,17 +7501,21 @@ minimalistic-crypto-utils@^1.0.1:
7445 resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" 7501 resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
7446 integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= 7502 integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
7447 7503
7448minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2: 7504minimatch@3.0.4, minimatch@^3.0.4:
7449 version "3.0.4" 7505 version "3.0.4"
7450 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 7506 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
7451 integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 7507 integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
7452 dependencies: 7508 dependencies:
7453 brace-expansion "^1.1.7" 7509 brace-expansion "^1.1.7"
7454 7510
7455minimist@1.1.x: 7511minimist-options@4.1.0:
7456 version "1.1.3" 7512 version "4.1.0"
7457 resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" 7513 resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
7458 integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag= 7514 integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==
7515 dependencies:
7516 arrify "^1.0.1"
7517 is-plain-obj "^1.1.0"
7518 kind-of "^6.0.3"
7459 7519
7460minimist@^1.2.0, minimist@^1.2.5: 7520minimist@^1.2.0, minimist@^1.2.5:
7461 version "1.2.5" 7521 version "1.2.5"
@@ -7553,7 +7613,7 @@ mkdirp-classic@^0.5.2:
7553 resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" 7613 resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
7554 integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== 7614 integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
7555 7615
7556mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: 7616mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
7557 version "0.5.5" 7617 version "0.5.5"
7558 resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 7618 resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
7559 integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 7619 integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -7655,11 +7715,6 @@ multistream@^4.0.1, multistream@^4.1.0:
7655 once "^1.4.0" 7715 once "^1.4.0"
7656 readable-stream "^3.6.0" 7716 readable-stream "^3.6.0"
7657 7717
7658mute-stream@0.0.5:
7659 version "0.0.5"
7660 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
7661 integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=
7662
7663mute-stream@0.0.8: 7718mute-stream@0.0.8:
7664 version "0.0.8" 7719 version "0.0.8"
7665 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" 7720 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
@@ -7818,7 +7873,7 @@ nopt@^5.0.0:
7818 dependencies: 7873 dependencies:
7819 abbrev "1" 7874 abbrev "1"
7820 7875
7821normalize-package-data@^2.3.2: 7876normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
7822 version "2.5.0" 7877 version "2.5.0"
7823 resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" 7878 resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
7824 integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== 7879 integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
@@ -7828,6 +7883,16 @@ normalize-package-data@^2.3.2:
7828 semver "2 || 3 || 4 || 5" 7883 semver "2 || 3 || 4 || 5"
7829 validate-npm-package-license "^3.0.1" 7884 validate-npm-package-license "^3.0.1"
7830 7885
7886normalize-package-data@^3.0.0:
7887 version "3.0.2"
7888 resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699"
7889 integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==
7890 dependencies:
7891 hosted-git-info "^4.0.1"
7892 resolve "^1.20.0"
7893 semver "^7.3.4"
7894 validate-npm-package-license "^3.0.1"
7895
7831normalize-path@^2.1.1: 7896normalize-path@^2.1.1:
7832 version "2.1.1" 7897 version "2.1.1"
7833 resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" 7898 resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
@@ -7845,6 +7910,11 @@ normalize-range@^0.1.2:
7845 resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" 7910 resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
7846 integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= 7911 integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
7847 7912
7913normalize-selector@^0.2.0:
7914 version "0.2.0"
7915 resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03"
7916 integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=
7917
7848normalize-url@^3.0.0: 7918normalize-url@^3.0.0:
7849 version "3.3.0" 7919 version "3.3.0"
7850 resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" 7920 resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
@@ -7961,6 +8031,11 @@ nth-check@^1.0.2:
7961 dependencies: 8031 dependencies:
7962 boolbase "~1.0.0" 8032 boolbase "~1.0.0"
7963 8033
8034num2fraction@^1.2.2:
8035 version "1.2.2"
8036 resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
8037 integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
8038
7964number-is-nan@^1.0.0: 8039number-is-nan@^1.0.0:
7965 version "1.0.1" 8040 version "1.0.1"
7966 resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 8041 resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -8070,11 +8145,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
8070 dependencies: 8145 dependencies:
8071 wrappy "1" 8146 wrappy "1"
8072 8147
8073onetime@^1.0.0:
8074 version "1.1.0"
8075 resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
8076 integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=
8077
8078onetime@^5.1.0, onetime@^5.1.2: 8148onetime@^5.1.0, onetime@^5.1.2:
8079 version "5.1.2" 8149 version "5.1.2"
8080 resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" 8150 resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
@@ -8102,18 +8172,6 @@ opn@^5.5.0:
8102 dependencies: 8172 dependencies:
8103 is-wsl "^1.1.0" 8173 is-wsl "^1.1.0"
8104 8174
8105optionator@^0.8.1:
8106 version "0.8.3"
8107 resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
8108 integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
8109 dependencies:
8110 deep-is "~0.1.3"
8111 fast-levenshtein "~2.0.6"
8112 levn "~0.3.0"
8113 prelude-ls "~1.1.2"
8114 type-check "~0.3.2"
8115 word-wrap "~1.2.3"
8116
8117ora@5.3.0: 8175ora@5.3.0:
8118 version "5.3.0" 8176 version "5.3.0"
8119 resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" 8177 resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f"
@@ -8140,11 +8198,6 @@ os-browserify@^0.3.0:
8140 resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" 8198 resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
8141 integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= 8199 integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
8142 8200
8143os-homedir@^1.0.0:
8144 version "1.0.2"
8145 resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
8146 integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
8147
8148os-locale@^2.0.0: 8201os-locale@^2.0.0:
8149 version "2.1.0" 8202 version "2.1.0"
8150 resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" 8203 resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
@@ -8328,6 +8381,18 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
8328 pbkdf2 "^3.0.3" 8381 pbkdf2 "^3.0.3"
8329 safe-buffer "^5.1.1" 8382 safe-buffer "^5.1.1"
8330 8383
8384parse-entities@^2.0.0:
8385 version "2.0.0"
8386 resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8"
8387 integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==
8388 dependencies:
8389 character-entities "^1.0.0"
8390 character-entities-legacy "^1.0.0"
8391 character-reference-invalid "^1.0.0"
8392 is-alphanumerical "^1.0.0"
8393 is-decimal "^1.0.0"
8394 is-hexadecimal "^1.0.0"
8395
8331parse-json@^2.2.0: 8396parse-json@^2.2.0:
8332 version "2.2.0" 8397 version "2.2.0"
8333 resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 8398 resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
@@ -8572,11 +8637,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
8572 dependencies: 8637 dependencies:
8573 find-up "^4.0.0" 8638 find-up "^4.0.0"
8574 8639
8575pluralize@^1.2.1:
8576 version "1.2.1"
8577 resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
8578 integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=
8579
8580pngjs@^3.3.0: 8640pngjs@^3.3.0:
8581 version "3.4.0" 8641 version "3.4.0"
8582 resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" 8642 resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
@@ -8659,6 +8719,13 @@ postcss-discard-overridden@^4.0.1:
8659 dependencies: 8719 dependencies:
8660 postcss "^7.0.0" 8720 postcss "^7.0.0"
8661 8721
8722postcss-html@^0.36.0:
8723 version "0.36.0"
8724 resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-0.36.0.tgz#b40913f94eaacc2453fd30a1327ad6ee1f88b204"
8725 integrity sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==
8726 dependencies:
8727 htmlparser2 "^3.10.0"
8728
8662postcss-import@14.0.0: 8729postcss-import@14.0.0:
8663 version "14.0.0" 8730 version "14.0.0"
8664 resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.0.0.tgz#3ed1dadac5a16650bde3f4cdea6633b9c3c78296" 8731 resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.0.0.tgz#3ed1dadac5a16650bde3f4cdea6633b9c3c78296"
@@ -8668,6 +8735,13 @@ postcss-import@14.0.0:
8668 read-cache "^1.0.0" 8735 read-cache "^1.0.0"
8669 resolve "^1.1.7" 8736 resolve "^1.1.7"
8670 8737
8738postcss-less@^3.1.4:
8739 version "3.1.4"
8740 resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-3.1.4.tgz#369f58642b5928ef898ffbc1a6e93c958304c5ad"
8741 integrity sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==
8742 dependencies:
8743 postcss "^7.0.14"
8744
8671postcss-loader@4.2.0: 8745postcss-loader@4.2.0:
8672 version "4.2.0" 8746 version "4.2.0"
8673 resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.2.0.tgz#f6993ea3e0f46600fb3ee49bbd010448123a7db4" 8747 resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.2.0.tgz#f6993ea3e0f46600fb3ee49bbd010448123a7db4"
@@ -8679,6 +8753,11 @@ postcss-loader@4.2.0:
8679 schema-utils "^3.0.0" 8753 schema-utils "^3.0.0"
8680 semver "^7.3.4" 8754 semver "^7.3.4"
8681 8755
8756postcss-media-query-parser@^0.2.3:
8757 version "0.2.3"
8758 resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
8759 integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=
8760
8682postcss-merge-longhand@^4.0.11: 8761postcss-merge-longhand@^4.0.11:
8683 version "4.0.11" 8762 version "4.0.11"
8684 resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" 8763 resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24"
@@ -8879,6 +8958,33 @@ postcss-reduce-transforms@^4.0.2:
8879 postcss "^7.0.0" 8958 postcss "^7.0.0"
8880 postcss-value-parser "^3.0.0" 8959 postcss-value-parser "^3.0.0"
8881 8960
8961postcss-resolve-nested-selector@^0.1.1:
8962 version "0.1.1"
8963 resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e"
8964 integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=
8965
8966postcss-safe-parser@^4.0.2:
8967 version "4.0.2"
8968 resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96"
8969 integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==
8970 dependencies:
8971 postcss "^7.0.26"
8972
8973postcss-sass@^0.4.4:
8974 version "0.4.4"
8975 resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.4.4.tgz#91f0f3447b45ce373227a98b61f8d8f0785285a3"
8976 integrity sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==
8977 dependencies:
8978 gonzales-pe "^4.3.0"
8979 postcss "^7.0.21"
8980
8981postcss-scss@^2.1.1:
8982 version "2.1.1"
8983 resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-2.1.1.tgz#ec3a75fa29a55e016b90bf3269026c53c1d2b383"
8984 integrity sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==
8985 dependencies:
8986 postcss "^7.0.6"
8987
8882postcss-selector-parser@^3.0.0: 8988postcss-selector-parser@^3.0.0:
8883 version "3.1.2" 8989 version "3.1.2"
8884 resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" 8990 resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270"
@@ -8898,6 +9004,22 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
8898 uniq "^1.0.1" 9004 uniq "^1.0.1"
8899 util-deprecate "^1.0.2" 9005 util-deprecate "^1.0.2"
8900 9006
9007postcss-selector-parser@^6.0.5:
9008 version "6.0.5"
9009 resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz#042d74e137db83e6f294712096cb413f5aa612c4"
9010 integrity sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==
9011 dependencies:
9012 cssesc "^3.0.0"
9013 util-deprecate "^1.0.2"
9014
9015postcss-sorting@^5.0.1:
9016 version "5.0.1"
9017 resolved "https://registry.yarnpkg.com/postcss-sorting/-/postcss-sorting-5.0.1.tgz#10d5d0059eea8334dacc820c0121864035bc3f11"
9018 integrity sha512-Y9fUFkIhfrm6i0Ta3n+89j56EFqaNRdUKqXyRp6kvTcSXnmgEjaVowCXH+JBe9+YKWqd4nc28r2sgwnzJalccA==
9019 dependencies:
9020 lodash "^4.17.14"
9021 postcss "^7.0.17"
9022
8901postcss-svgo@^4.0.3: 9023postcss-svgo@^4.0.3:
8902 version "4.0.3" 9024 version "4.0.3"
8903 resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" 9025 resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e"
@@ -8907,6 +9029,11 @@ postcss-svgo@^4.0.3:
8907 postcss-value-parser "^3.0.0" 9029 postcss-value-parser "^3.0.0"
8908 svgo "^1.0.0" 9030 svgo "^1.0.0"
8909 9031
9032postcss-syntax@^0.36.2:
9033 version "0.36.2"
9034 resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c"
9035 integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==
9036
8910postcss-unique-selectors@^4.0.1: 9037postcss-unique-selectors@^4.0.1:
8911 version "4.0.1" 9038 version "4.0.1"
8912 resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" 9039 resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac"
@@ -8944,7 +9071,7 @@ postcss@8.2.4:
8944 nanoid "^3.1.20" 9071 nanoid "^3.1.20"
8945 source-map "^0.6.1" 9072 source-map "^0.6.1"
8946 9073
8947postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: 9074postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.31, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0.6:
8948 version "7.0.35" 9075 version "7.0.35"
8949 resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" 9076 resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
8950 integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== 9077 integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
@@ -8962,11 +9089,6 @@ postcss@^8.0.2, postcss@^8.1.4, postcss@^8.2.8:
8962 nanoid "^3.1.22" 9089 nanoid "^3.1.22"
8963 source-map "^0.6.1" 9090 source-map "^0.6.1"
8964 9091
8965prelude-ls@~1.1.2:
8966 version "1.1.2"
8967 resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
8968 integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
8969
8970pretty-bytes@^5.3.0: 9092pretty-bytes@^5.3.0:
8971 version "5.6.0" 9093 version "5.6.0"
8972 resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" 9094 resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
@@ -9002,11 +9124,6 @@ process@~0.5.1:
9002 resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" 9124 resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
9003 integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= 9125 integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=
9004 9126
9005progress@^1.1.8:
9006 version "1.1.8"
9007 resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
9008 integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
9009
9010promise-inflight@^1.0.1: 9127promise-inflight@^1.0.1:
9011 version "1.0.1" 9128 version "1.0.1"
9012 resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" 9129 resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
@@ -9190,6 +9307,11 @@ queue-microtask@^1.2.0, queue-microtask@^1.2.2:
9190 resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 9307 resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
9191 integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 9308 integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
9192 9309
9310quick-lru@^4.0.1:
9311 version "4.0.1"
9312 resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
9313 integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
9314
9193random-access-file@^2.0.1: 9315random-access-file@^2.0.1:
9194 version "2.2.0" 9316 version "2.2.0"
9195 resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.2.0.tgz#b49b999efefb374afb7587f219071fec5ce66546" 9317 resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.2.0.tgz#b49b999efefb374afb7587f219071fec5ce66546"
@@ -9286,6 +9408,15 @@ read-pkg-up@^2.0.0:
9286 find-up "^2.0.0" 9408 find-up "^2.0.0"
9287 read-pkg "^2.0.0" 9409 read-pkg "^2.0.0"
9288 9410
9411read-pkg-up@^7.0.1:
9412 version "7.0.1"
9413 resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
9414 integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==
9415 dependencies:
9416 find-up "^4.1.0"
9417 read-pkg "^5.2.0"
9418 type-fest "^0.8.1"
9419
9289read-pkg@^2.0.0: 9420read-pkg@^2.0.0:
9290 version "2.0.0" 9421 version "2.0.0"
9291 resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" 9422 resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
@@ -9295,6 +9426,16 @@ read-pkg@^2.0.0:
9295 normalize-package-data "^2.3.2" 9426 normalize-package-data "^2.3.2"
9296 path-type "^2.0.0" 9427 path-type "^2.0.0"
9297 9428
9429read-pkg@^5.2.0:
9430 version "5.2.0"
9431 resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
9432 integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
9433 dependencies:
9434 "@types/normalize-package-data" "^2.4.0"
9435 normalize-package-data "^2.5.0"
9436 parse-json "^5.0.0"
9437 type-fest "^0.6.0"
9438
9298"readable-stream@1 || 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.3, readable-stream@^2.3.6, readable-stream@~2.3.6: 9439"readable-stream@1 || 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.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
9299 version "2.3.7" 9440 version "2.3.7"
9300 resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 9441 resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
@@ -9333,15 +9474,6 @@ readdirp@~3.5.0:
9333 dependencies: 9474 dependencies:
9334 picomatch "^2.2.1" 9475 picomatch "^2.2.1"
9335 9476
9336readline2@^1.0.1:
9337 version "1.0.1"
9338 resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35"
9339 integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=
9340 dependencies:
9341 code-point-at "^1.0.0"
9342 is-fullwidth-code-point "^1.0.0"
9343 mute-stream "0.0.5"
9344
9345rechoir@^0.7.0: 9477rechoir@^0.7.0:
9346 version "0.7.0" 9478 version "0.7.0"
9347 resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" 9479 resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
@@ -9354,6 +9486,14 @@ record-cache@^1.0.2:
9354 resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.0.tgz#f8a467a691a469584b26e88d36b18afdb3932037" 9486 resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.0.tgz#f8a467a691a469584b26e88d36b18afdb3932037"
9355 integrity sha512-u8rbtLEJV7HRacl/ZYwSBFD8NFyB3PfTTfGLP37IW3hftQCwu6z4Q2RLyxo1YJUNRTEzJfpLpGwVuEYdaIkG9Q== 9487 integrity sha512-u8rbtLEJV7HRacl/ZYwSBFD8NFyB3PfTTfGLP37IW3hftQCwu6z4Q2RLyxo1YJUNRTEzJfpLpGwVuEYdaIkG9Q==
9356 9488
9489redent@^3.0.0:
9490 version "3.0.0"
9491 resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
9492 integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
9493 dependencies:
9494 indent-string "^4.0.0"
9495 strip-indent "^3.0.0"
9496
9357reflect-metadata@^0.1.2: 9497reflect-metadata@^0.1.2:
9358 version "0.1.13" 9498 version "0.1.13"
9359 resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" 9499 resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
@@ -9433,6 +9573,29 @@ relateurl@^0.2.7:
9433 resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" 9573 resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
9434 integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= 9574 integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
9435 9575
9576remark-parse@^9.0.0:
9577 version "9.0.0"
9578 resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640"
9579 integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==
9580 dependencies:
9581 mdast-util-from-markdown "^0.8.0"
9582
9583remark-stringify@^9.0.0:
9584 version "9.0.1"
9585 resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894"
9586 integrity sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==
9587 dependencies:
9588 mdast-util-to-markdown "^0.6.0"
9589
9590remark@^13.0.0:
9591 version "13.0.0"
9592 resolved "https://registry.yarnpkg.com/remark/-/remark-13.0.0.tgz#d15d9bf71a402f40287ebe36067b66d54868e425"
9593 integrity sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==
9594 dependencies:
9595 remark-parse "^9.0.0"
9596 remark-stringify "^9.0.0"
9597 unified "^9.1.0"
9598
9436remove-trailing-separator@^1.0.1: 9599remove-trailing-separator@^1.0.1:
9437 version "1.1.0" 9600 version "1.1.0"
9438 resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" 9601 resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
@@ -9472,7 +9635,7 @@ repeat-element@^1.1.2:
9472 resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" 9635 resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9"
9473 integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== 9636 integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==
9474 9637
9475repeat-string@^1.6.1: 9638repeat-string@^1.0.0, repeat-string@^1.6.1:
9476 version "1.6.1" 9639 version "1.6.1"
9477 resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 9640 resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
9478 integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= 9641 integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
@@ -9508,6 +9671,11 @@ require-directory@^2.1.1:
9508 resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 9671 resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
9509 integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 9672 integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
9510 9673
9674require-from-string@^2.0.2:
9675 version "2.0.2"
9676 resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
9677 integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
9678
9511require-main-filename@^1.0.1: 9679require-main-filename@^1.0.1:
9512 version "1.0.1" 9680 version "1.0.1"
9513 resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" 9681 resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
@@ -9518,14 +9686,6 @@ require-main-filename@^2.0.0:
9518 resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" 9686 resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
9519 integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== 9687 integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
9520 9688
9521require-uncached@^1.0.2:
9522 version "1.0.3"
9523 resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
9524 integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=
9525 dependencies:
9526 caller-path "^0.1.0"
9527 resolve-from "^1.0.0"
9528
9529requires-port@^1.0.0: 9689requires-port@^1.0.0:
9530 version "1.0.0" 9690 version "1.0.0"
9531 resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" 9691 resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
@@ -9545,11 +9705,6 @@ resolve-cwd@^3.0.0:
9545 dependencies: 9705 dependencies:
9546 resolve-from "^5.0.0" 9706 resolve-from "^5.0.0"
9547 9707
9548resolve-from@^1.0.0:
9549 version "1.0.1"
9550 resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
9551 integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=
9552
9553resolve-from@^3.0.0: 9708resolve-from@^3.0.0:
9554 version "3.0.0" 9709 version "3.0.0"
9555 resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" 9710 resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
@@ -9594,7 +9749,7 @@ resolve@1.19.0:
9594 is-core-module "^2.1.0" 9749 is-core-module "^2.1.0"
9595 path-parse "^1.0.6" 9750 path-parse "^1.0.6"
9596 9751
9597resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.9.0: 9752resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.9.0:
9598 version "1.20.0" 9753 version "1.20.0"
9599 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" 9754 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
9600 integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== 9755 integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@@ -9602,14 +9757,6 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.9.0:
9602 is-core-module "^2.2.0" 9757 is-core-module "^2.2.0"
9603 path-parse "^1.0.6" 9758 path-parse "^1.0.6"
9604 9759
9605restore-cursor@^1.0.1:
9606 version "1.0.1"
9607 resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
9608 integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=
9609 dependencies:
9610 exit-hook "^1.0.0"
9611 onetime "^1.0.0"
9612
9613restore-cursor@^3.1.0: 9760restore-cursor@^3.1.0:
9614 version "3.1.0" 9761 version "3.1.0"
9615 resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" 9762 resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@@ -9680,13 +9827,6 @@ rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.3:
9680 dependencies: 9827 dependencies:
9681 glob "^7.1.3" 9828 glob "^7.1.3"
9682 9829
9683rimraf@~2.6.2:
9684 version "2.6.3"
9685 resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
9686 integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
9687 dependencies:
9688 glob "^7.1.3"
9689
9690ripemd160@^2.0.0, ripemd160@^2.0.1: 9830ripemd160@^2.0.0, ripemd160@^2.0.1:
9691 version "2.0.2" 9831 version "2.0.2"
9692 resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" 9832 resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -9717,13 +9857,6 @@ rollup@2.38.4:
9717 optionalDependencies: 9857 optionalDependencies:
9718 fsevents "~2.3.1" 9858 fsevents "~2.3.1"
9719 9859
9720run-async@^0.1.0:
9721 version "0.1.0"
9722 resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389"
9723 integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=
9724 dependencies:
9725 once "^1.3.0"
9726
9727run-async@^2.4.0: 9860run-async@^2.4.0:
9728 version "2.4.1" 9861 version "2.4.1"
9729 resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" 9862 resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@@ -9767,11 +9900,6 @@ rust-result@^1.0.0:
9767 dependencies: 9900 dependencies:
9768 individual "^2.0.0" 9901 individual "^2.0.0"
9769 9902
9770rx-lite@^3.1.2:
9771 version "3.1.2"
9772 resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
9773 integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=
9774
9775rxjs@6.6.3: 9903rxjs@6.6.3:
9776 version "6.6.3" 9904 version "6.6.3"
9777 resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" 9905 resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
@@ -9828,26 +9956,6 @@ sanitize-html@^2.1.2:
9828 parse-srcset "^1.0.2" 9956 parse-srcset "^1.0.2"
9829 postcss "^8.0.2" 9957 postcss "^8.0.2"
9830 9958
9831sass-lint@^1.13.1:
9832 version "1.13.1"
9833 resolved "https://registry.yarnpkg.com/sass-lint/-/sass-lint-1.13.1.tgz#5fd2b2792e9215272335eb0f0dc607f61e8acc8f"
9834 integrity sha512-DSyah8/MyjzW2BWYmQWekYEKir44BpLqrCFsgs9iaWiVTcwZfwXHF586hh3D1n+/9ihUNMfd8iHAyb9KkGgs7Q==
9835 dependencies:
9836 commander "^2.8.1"
9837 eslint "^2.7.0"
9838 front-matter "2.1.2"
9839 fs-extra "^3.0.1"
9840 glob "^7.0.0"
9841 globule "^1.0.0"
9842 gonzales-pe-sl "^4.2.3"
9843 js-yaml "^3.5.4"
9844 known-css-properties "^0.3.0"
9845 lodash.capitalize "^4.1.0"
9846 lodash.kebabcase "^4.0.0"
9847 merge "^1.2.0"
9848 path-is-absolute "^1.0.0"
9849 util "^0.10.3"
9850
9851sass-loader@10.1.1, sass-loader@^10: 9959sass-loader@10.1.1, sass-loader@^10:
9852 version "10.1.1" 9960 version "10.1.1"
9853 resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" 9961 resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d"
@@ -10125,11 +10233,6 @@ shebang-regex@^3.0.0:
10125 resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 10233 resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
10126 integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 10234 integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
10127 10235
10128shelljs@^0.6.0:
10129 version "0.6.1"
10130 resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8"
10131 integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=
10132
10133signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: 10236signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
10134 version "3.0.3" 10237 version "3.0.3"
10135 resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 10238 resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
@@ -10202,10 +10305,14 @@ slash@^3.0.0:
10202 resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 10305 resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
10203 integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 10306 integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
10204 10307
10205slice-ansi@0.0.4: 10308slice-ansi@^4.0.0:
10206 version "0.0.4" 10309 version "4.0.0"
10207 resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" 10310 resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
10208 integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= 10311 integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
10312 dependencies:
10313 ansi-styles "^4.0.0"
10314 astral-regex "^2.0.0"
10315 is-fullwidth-code-point "^3.0.0"
10209 10316
10210smart-buffer@^4.1.0: 10317smart-buffer@^4.1.0:
10211 version "4.1.0" 10318 version "4.1.0"
@@ -10447,6 +10554,11 @@ spdy@^4.0.2:
10447 select-hose "^2.0.0" 10554 select-hose "^2.0.0"
10448 spdy-transport "^3.0.0" 10555 spdy-transport "^3.0.0"
10449 10556
10557specificity@^0.4.1:
10558 version "0.4.1"
10559 resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019"
10560 integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==
10561
10450speed-measure-webpack-plugin@1.4.2: 10562speed-measure-webpack-plugin@1.4.2:
10451 version "1.4.2" 10563 version "1.4.2"
10452 resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.4.2.tgz#1608e62d3bdb45f01810010e1b5bfedefedfa58f" 10564 resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.4.2.tgz#1608e62d3bdb45f01810010e1b5bfedefedfa58f"
@@ -10634,7 +10746,7 @@ string-width@^3.0.0, string-width@^3.1.0:
10634 is-fullwidth-code-point "^2.0.0" 10746 is-fullwidth-code-point "^2.0.0"
10635 strip-ansi "^5.1.0" 10747 strip-ansi "^5.1.0"
10636 10748
10637string-width@^4.1.0, string-width@^4.2.0: 10749string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2:
10638 version "4.2.2" 10750 version "4.2.2"
10639 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" 10751 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
10640 integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== 10752 integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
@@ -10724,10 +10836,12 @@ strip-final-newline@^2.0.0:
10724 resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" 10836 resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
10725 integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== 10837 integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
10726 10838
10727strip-json-comments@~1.0.1: 10839strip-indent@^3.0.0:
10728 version "1.0.4" 10840 version "3.0.0"
10729 resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" 10841 resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
10730 integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= 10842 integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
10843 dependencies:
10844 min-indent "^1.0.0"
10731 10845
10732style-loader@2.0.0: 10846style-loader@2.0.0:
10733 version "2.0.0" 10847 version "2.0.0"
@@ -10737,6 +10851,11 @@ style-loader@2.0.0:
10737 loader-utils "^2.0.0" 10851 loader-utils "^2.0.0"
10738 schema-utils "^3.0.0" 10852 schema-utils "^3.0.0"
10739 10853
10854style-search@^0.1.0:
10855 version "0.1.0"
10856 resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"
10857 integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=
10858
10740stylehacks@^4.0.0: 10859stylehacks@^4.0.0:
10741 version "4.0.3" 10860 version "4.0.3"
10742 resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" 10861 resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
@@ -10746,6 +10865,88 @@ stylehacks@^4.0.0:
10746 postcss "^7.0.0" 10865 postcss "^7.0.0"
10747 postcss-selector-parser "^3.0.0" 10866 postcss-selector-parser "^3.0.0"
10748 10867
10868stylelint-config-sass-guidelines@^8.0.0:
10869 version "8.0.0"
10870 resolved "https://registry.yarnpkg.com/stylelint-config-sass-guidelines/-/stylelint-config-sass-guidelines-8.0.0.tgz#e92279aa052a04e822dd096d7c46c8e37d4b3406"
10871 integrity sha512-v21iDWtzpfhuKJlYKpoE1vjp+GT8Cr6ZBWwMx/jf+eCEblZgAIDVVjgAELoDLhVj17DcEFwlIKJBMfrdAmXg0Q==
10872 dependencies:
10873 stylelint-order "^4.0.0"
10874 stylelint-scss "^3.18.0"
10875
10876stylelint-order@^4.0.0:
10877 version "4.1.0"
10878 resolved "https://registry.yarnpkg.com/stylelint-order/-/stylelint-order-4.1.0.tgz#692d05b7d0c235ac66fcf5ea1d9e5f08a76747f6"
10879 integrity sha512-sVTikaDvMqg2aJjh4r48jsdfmqLT+nqB1MOsaBnvM3OwLx4S+WXcsxsgk5w18h/OZoxZCxuyXMh61iBHcj9Qiw==
10880 dependencies:
10881 lodash "^4.17.15"
10882 postcss "^7.0.31"
10883 postcss-sorting "^5.0.1"
10884
10885stylelint-scss@^3.18.0:
10886 version "3.19.0"
10887 resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-3.19.0.tgz#528006d5a4c5a0f1f4d709b02fd3f626ed66d742"
10888 integrity sha512-Ic5bsmpS4wVucOw44doC1Yi9f5qbeVL4wPFiEOaUElgsOuLEN6Ofn/krKI8BeNL2gAn53Zu+IcVV4E345r6rBw==
10889 dependencies:
10890 lodash "^4.17.15"
10891 postcss-media-query-parser "^0.2.3"
10892 postcss-resolve-nested-selector "^0.1.1"
10893 postcss-selector-parser "^6.0.2"
10894 postcss-value-parser "^4.1.0"
10895
10896stylelint@^13.13.0:
10897 version "13.13.0"
10898 resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.13.0.tgz#1a33bffde765920ac985f16ae6250ff914b27804"
10899 integrity sha512-jvkM1iuH88vAvjdKPwPm6abiMP2/D/1chbfb+4GVONddOOskHuCXc0loyrLdxO1AwwH6jdnjYskkTKHQD7cXwQ==
10900 dependencies:
10901 "@stylelint/postcss-css-in-js" "^0.37.2"
10902 "@stylelint/postcss-markdown" "^0.36.2"
10903 autoprefixer "^9.8.6"
10904 balanced-match "^2.0.0"
10905 chalk "^4.1.0"
10906 cosmiconfig "^7.0.0"
10907 debug "^4.3.1"
10908 execall "^2.0.0"
10909 fast-glob "^3.2.5"
10910 fastest-levenshtein "^1.0.12"
10911 file-entry-cache "^6.0.1"
10912 get-stdin "^8.0.0"
10913 global-modules "^2.0.0"
10914 globby "^11.0.3"
10915 globjoin "^0.1.4"
10916 html-tags "^3.1.0"
10917 ignore "^5.1.8"
10918 import-lazy "^4.0.0"
10919 imurmurhash "^0.1.4"
10920 known-css-properties "^0.21.0"
10921 lodash "^4.17.21"
10922 log-symbols "^4.1.0"
10923 mathml-tag-names "^2.1.3"
10924 meow "^9.0.0"
10925 micromatch "^4.0.4"
10926 normalize-selector "^0.2.0"
10927 postcss "^7.0.35"
10928 postcss-html "^0.36.0"
10929 postcss-less "^3.1.4"
10930 postcss-media-query-parser "^0.2.3"
10931 postcss-resolve-nested-selector "^0.1.1"
10932 postcss-safe-parser "^4.0.2"
10933 postcss-sass "^0.4.4"
10934 postcss-scss "^2.1.1"
10935 postcss-selector-parser "^6.0.5"
10936 postcss-syntax "^0.36.2"
10937 postcss-value-parser "^4.1.0"
10938 resolve-from "^5.0.0"
10939 slash "^3.0.0"
10940 specificity "^0.4.1"
10941 string-width "^4.2.2"
10942 strip-ansi "^6.0.0"
10943 style-search "^0.1.0"
10944 sugarss "^2.0.0"
10945 svg-tags "^1.0.0"
10946 table "^6.5.1"
10947 v8-compile-cache "^2.3.0"
10948 write-file-atomic "^3.0.3"
10949
10749stylus-loader@4.3.3: 10950stylus-loader@4.3.3:
10750 version "4.3.3" 10951 version "4.3.3"
10751 resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.3.3.tgz#381bb6341272ac50bcdfd0b877707eac99b6b757" 10952 resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.3.3.tgz#381bb6341272ac50bcdfd0b877707eac99b6b757"
@@ -10771,6 +10972,13 @@ stylus@0.54.8:
10771 semver "^6.3.0" 10972 semver "^6.3.0"
10772 source-map "^0.7.3" 10973 source-map "^0.7.3"
10773 10974
10975sugarss@^2.0.0:
10976 version "2.0.0"
10977 resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d"
10978 integrity sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==
10979 dependencies:
10980 postcss "^7.0.2"
10981
10774supports-color@^2.0.0: 10982supports-color@^2.0.0:
10775 version "2.0.0" 10983 version "2.0.0"
10776 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 10984 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -10797,6 +11005,11 @@ supports-color@^7.0.0, supports-color@^7.1.0:
10797 dependencies: 11005 dependencies:
10798 has-flag "^4.0.0" 11006 has-flag "^4.0.0"
10799 11007
11008svg-tags@^1.0.0:
11009 version "1.0.0"
11010 resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
11011 integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
11012
10800svgo@^1.0.0: 11013svgo@^1.0.0:
10801 version "1.3.2" 11014 version "1.3.2"
10802 resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" 11015 resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
@@ -10821,17 +11034,18 @@ symbol-observable@3.0.0:
10821 resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-3.0.0.tgz#eea8f6478c651018e059044268375c408c15c533" 11034 resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-3.0.0.tgz#eea8f6478c651018e059044268375c408c15c533"
10822 integrity sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q== 11035 integrity sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q==
10823 11036
10824table@^3.7.8: 11037table@^6.5.1:
10825 version "3.8.3" 11038 version "6.6.0"
10826 resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" 11039 resolved "https://registry.yarnpkg.com/table/-/table-6.6.0.tgz#905654b79df98d9e9a973de1dd58682532c40e8e"
10827 integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= 11040 integrity sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg==
10828 dependencies: 11041 dependencies:
10829 ajv "^4.7.0" 11042 ajv "^8.0.1"
10830 ajv-keywords "^1.0.0" 11043 lodash.clonedeep "^4.5.0"
10831 chalk "^1.1.1" 11044 lodash.flatten "^4.4.0"
10832 lodash "^4.0.0" 11045 lodash.truncate "^4.4.2"
10833 slice-ansi "0.0.4" 11046 slice-ansi "^4.0.0"
10834 string-width "^2.0.0" 11047 string-width "^4.2.0"
11048 strip-ansi "^6.0.0"
10835 11049
10836tapable@^1.0.0, tapable@^1.1.3: 11050tapable@^1.0.0, tapable@^1.1.3:
10837 version "1.1.3" 11051 version "1.1.3"
@@ -10912,7 +11126,7 @@ terser@^5.3.4:
10912 source-map "~0.7.2" 11126 source-map "~0.7.2"
10913 source-map-support "~0.5.19" 11127 source-map-support "~0.5.19"
10914 11128
10915text-table@0.2.0, text-table@~0.2.0: 11129text-table@0.2.0:
10916 version "0.2.0" 11130 version "0.2.0"
10917 resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 11131 resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
10918 integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 11132 integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
@@ -11074,6 +11288,16 @@ tree-kill@1.2.2:
11074 resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" 11288 resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
11075 integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== 11289 integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
11076 11290
11291trim-newlines@^3.0.0:
11292 version "3.0.0"
11293 resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30"
11294 integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==
11295
11296trough@^1.0.0:
11297 version "1.0.5"
11298 resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
11299 integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
11300
11077ts-loader@^8.0.14: 11301ts-loader@^8.0.14:
11078 version "8.1.0" 11302 version "8.1.0"
11079 resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.1.0.tgz#d6292487df279c7cc79b6d3b70bb9d31682b693e" 11303 resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.1.0.tgz#d6292487df279c7cc79b6d3b70bb9d31682b693e"
@@ -11181,18 +11405,26 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
11181 resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" 11405 resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
11182 integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= 11406 integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
11183 11407
11184type-check@~0.3.2: 11408type-fest@^0.18.0:
11185 version "0.3.2" 11409 version "0.18.1"
11186 resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 11410 resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"
11187 integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= 11411 integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==
11188 dependencies:
11189 prelude-ls "~1.1.2"
11190 11412
11191type-fest@^0.21.3: 11413type-fest@^0.21.3:
11192 version "0.21.3" 11414 version "0.21.3"
11193 resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" 11415 resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
11194 integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== 11416 integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
11195 11417
11418type-fest@^0.6.0:
11419 version "0.6.0"
11420 resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
11421 integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
11422
11423type-fest@^0.8.1:
11424 version "0.8.1"
11425 resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
11426 integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
11427
11196type-is@~1.6.17, type-is@~1.6.18: 11428type-is@~1.6.17, type-is@~1.6.18:
11197 version "1.6.18" 11429 version "1.6.18"
11198 resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 11430 resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
@@ -11211,7 +11443,7 @@ type@^2.0.0:
11211 resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" 11443 resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d"
11212 integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== 11444 integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
11213 11445
11214typedarray-to-buffer@^3.0.0: 11446typedarray-to-buffer@^3.0.0, typedarray-to-buffer@^3.1.5:
11215 version "3.1.5" 11447 version "3.1.5"
11216 resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" 11448 resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
11217 integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== 11449 integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
@@ -11283,6 +11515,18 @@ unicode-property-aliases-ecmascript@^1.0.4:
11283 resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" 11515 resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
11284 integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== 11516 integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
11285 11517
11518unified@^9.1.0:
11519 version "9.2.1"
11520 resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.1.tgz#ae18d5674c114021bfdbdf73865ca60f410215a3"
11521 integrity sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==
11522 dependencies:
11523 bail "^1.0.0"
11524 extend "^3.0.0"
11525 is-buffer "^2.0.0"
11526 is-plain-obj "^2.0.0"
11527 trough "^1.0.0"
11528 vfile "^4.0.0"
11529
11286union-value@^1.0.0: 11530union-value@^1.0.0:
11287 version "1.0.1" 11531 version "1.0.1"
11288 resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" 11532 resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
@@ -11317,6 +11561,25 @@ unique-slug@^2.0.0:
11317 dependencies: 11561 dependencies:
11318 imurmurhash "^0.1.4" 11562 imurmurhash "^0.1.4"
11319 11563
11564unist-util-find-all-after@^3.0.2:
11565 version "3.0.2"
11566 resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz#fdfecd14c5b7aea5e9ef38d5e0d5f774eeb561f6"
11567 integrity sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==
11568 dependencies:
11569 unist-util-is "^4.0.0"
11570
11571unist-util-is@^4.0.0:
11572 version "4.1.0"
11573 resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797"
11574 integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==
11575
11576unist-util-stringify-position@^2.0.0:
11577 version "2.0.3"
11578 resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da"
11579 integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==
11580 dependencies:
11581 "@types/unist" "^2.0.2"
11582
11320universal-analytics@0.4.23: 11583universal-analytics@0.4.23:
11321 version "0.4.23" 11584 version "0.4.23"
11322 resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" 11585 resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac"
@@ -11402,13 +11665,6 @@ use@^3.1.0:
11402 resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" 11665 resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
11403 integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== 11666 integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
11404 11667
11405user-home@^2.0.0:
11406 version "2.0.0"
11407 resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
11408 integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8=
11409 dependencies:
11410 os-homedir "^1.0.0"
11411
11412ut_metadata@^3.5.2: 11668ut_metadata@^3.5.2:
11413 version "3.5.2" 11669 version "3.5.2"
11414 resolved "https://registry.yarnpkg.com/ut_metadata/-/ut_metadata-3.5.2.tgz#2351c9348759e929978fa6a08d56ef6f584749e7" 11670 resolved "https://registry.yarnpkg.com/ut_metadata/-/ut_metadata-3.5.2.tgz#2351c9348759e929978fa6a08d56ef6f584749e7"
@@ -11465,13 +11721,6 @@ util@0.10.3:
11465 dependencies: 11721 dependencies:
11466 inherits "2.0.1" 11722 inherits "2.0.1"
11467 11723
11468util@^0.10.3:
11469 version "0.10.4"
11470 resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
11471 integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
11472 dependencies:
11473 inherits "2.0.3"
11474
11475util@^0.11.0: 11724util@^0.11.0:
11476 version "0.11.1" 11725 version "0.11.1"
11477 resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" 11726 resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
@@ -11510,7 +11759,7 @@ uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0:
11510 resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" 11759 resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
11511 integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== 11760 integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
11512 11761
11513v8-compile-cache@^2.2.0: 11762v8-compile-cache@^2.2.0, v8-compile-cache@^2.3.0:
11514 version "2.3.0" 11763 version "2.3.0"
11515 resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" 11764 resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
11516 integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== 11765 integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
@@ -11549,6 +11798,24 @@ verror@1.10.0:
11549 core-util-is "1.0.2" 11798 core-util-is "1.0.2"
11550 extsprintf "^1.2.0" 11799 extsprintf "^1.2.0"
11551 11800
11801vfile-message@^2.0.0:
11802 version "2.0.4"
11803 resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a"
11804 integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==
11805 dependencies:
11806 "@types/unist" "^2.0.0"
11807 unist-util-stringify-position "^2.0.0"
11808
11809vfile@^4.0.0:
11810 version "4.2.1"
11811 resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624"
11812 integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==
11813 dependencies:
11814 "@types/unist" "^2.0.0"
11815 is-buffer "^2.0.0"
11816 unist-util-stringify-position "^2.0.0"
11817 vfile-message "^2.0.0"
11818
11552"video.js@^6 || ^7", video.js@^7, video.js@^7.6.0: 11819"video.js@^6 || ^7", video.js@^7, video.js@^7.6.0:
11553 version "7.11.8" 11820 version "7.11.8"
11554 resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.11.8.tgz#1fa27c56f30a436b06b44f21560f223e264aec51" 11821 resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.11.8.tgz#1fa27c56f30a436b06b44f21560f223e264aec51"
@@ -11938,7 +12205,7 @@ which-module@^2.0.0:
11938 resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 12205 resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
11939 integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= 12206 integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
11940 12207
11941which@^1.2.1, which@^1.2.9: 12208which@^1.2.1, which@^1.2.9, which@^1.3.1:
11942 version "1.3.1" 12209 version "1.3.1"
11943 resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 12210 resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
11944 integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 12211 integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -11964,11 +12231,6 @@ wildcard@^2.0.0:
11964 resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" 12231 resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
11965 integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== 12232 integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
11966 12233
11967word-wrap@~1.2.3:
11968 version "1.2.3"
11969 resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
11970 integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
11971
11972worker-farm@^1.7.0: 12234worker-farm@^1.7.0:
11973 version "1.7.0" 12235 version "1.7.0"
11974 resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" 12236 resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
@@ -12023,12 +12285,15 @@ wrappy@1:
12023 resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 12285 resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
12024 integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 12286 integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
12025 12287
12026write@^0.2.1: 12288write-file-atomic@^3.0.3:
12027 version "0.2.1" 12289 version "3.0.3"
12028 resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 12290 resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
12029 integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= 12291 integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
12030 dependencies: 12292 dependencies:
12031 mkdirp "^0.5.1" 12293 imurmurhash "^0.1.4"
12294 is-typedarray "^1.0.0"
12295 signal-exit "^3.0.2"
12296 typedarray-to-buffer "^3.1.5"
12032 12297
12033ws@^6.2.1: 12298ws@^6.2.1:
12034 version "6.2.1" 12299 version "6.2.1"
@@ -12121,7 +12386,7 @@ yargs-parser@^18.1.2:
12121 camelcase "^5.0.0" 12386 camelcase "^5.0.0"
12122 decamelize "^1.2.0" 12387 decamelize "^1.2.0"
12123 12388
12124yargs-parser@^20.2.2: 12389yargs-parser@^20.2.2, yargs-parser@^20.2.3:
12125 version "20.2.7" 12390 version "20.2.7"
12126 resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" 12391 resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
12127 integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== 12392 integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
@@ -12219,3 +12484,8 @@ zone.js@~0.11.3:
12219 integrity sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw== 12484 integrity sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==
12220 dependencies: 12485 dependencies:
12221 tslib "^2.0.0" 12486 tslib "^2.0.0"
12487
12488zwitch@^1.0.0:
12489 version "1.0.5"
12490 resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
12491 integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==
diff --git a/package.json b/package.json
index 9481e63f4..8552a9d60 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
5 "private": true, 5 "private": true,
6 "licence": "AGPL-3.0", 6 "licence": "AGPL-3.0",
7 "engines": { 7 "engines": {
8 "node": ">=10.x <=15", 8 "node": ">=10.x",
9 "yarn": ">=1.x", 9 "yarn": ">=1.x",
10 "postgres": ">=10.x", 10 "postgres": ">=10.x",
11 "redis-server": ">=2.8.18", 11 "redis-server": ">=2.8.18",
@@ -25,30 +25,25 @@
25 }, 25 },
26 "typings": "*.d.ts", 26 "typings": "*.d.ts",
27 "scripts": { 27 "scripts": {
28 "e2e": "scripty", 28 "e2e": "sh ./scripts/e2e/index.sh",
29 "e2e:local": "scripty", 29 "e2e:local": "sh ./scripts/e2e/local.sh",
30 "setup:cli": "scripty", 30 "setup:cli": "sh ./scripts/setup/cli.sh",
31 "build": "scripty", 31 "build": "sh ./scripts/build/index.sh",
32 "build:embed": "scripty", 32 "build:embed": "sh ./scripts/build/embed.sh",
33 "build:server": "scripty", 33 "build:server": "sh ./scripts/build/server.sh",
34 "build:client": "scripty", 34 "build:client": "sh ./scripts/build/client.sh",
35 "clean:client": "scripty", 35 "clean:client": "sh ./scripts/clean/client/index.sh",
36 "clean:server": "scripty", 36 "clean:server:test": "sh ./scripts/clean/server/test.sh",
37 "clean:server:test": "scripty", 37 "i18n:update": "sh ./scripts/i18n/update.sh",
38 "danger:clean:dev": "scripty",
39 "danger:clean:prod": "scripty",
40 "danger:clean:modules": "scripty",
41 "i18n:update": "scripty",
42 "plugin:install": "node ./dist/scripts/plugin/install.js", 38 "plugin:install": "node ./dist/scripts/plugin/install.js",
43 "plugin:uninstall": "node ./dist/scripts/plugin/uninstall.js", 39 "plugin:uninstall": "node ./dist/scripts/plugin/uninstall.js",
44 "i18n:create-custom-files": "node ./dist/scripts/i18n/create-custom-files.js", 40 "i18n:create-custom-files": "node ./dist/scripts/i18n/create-custom-files.js",
45 "reset-password": "node ./dist/scripts/reset-password.js", 41 "reset-password": "node ./dist/scripts/reset-password.js",
46 "play": "scripty",
47 "dev": "sh ./scripts/dev/index.sh", 42 "dev": "sh ./scripts/dev/index.sh",
48 "dev:server": "sh ./scripts/dev/server.sh", 43 "dev:server": "sh ./scripts/dev/server.sh",
49 "dev:embed": "scripty", 44 "dev:embed": "sh ./scripts/dev/embed.sh",
50 "dev:client": "sh ./scripts/dev/client.sh", 45 "dev:client": "sh ./scripts/dev/client.sh",
51 "dev:cli": "scripty", 46 "dev:cli": "sh ./scripts/dev/cli.sh",
52 "start": "node dist/server", 47 "start": "node dist/server",
53 "start:server": "node dist/server --no-client", 48 "start:server": "node dist/server --no-client",
54 "update-host": "node ./dist/scripts/update-host.js", 49 "update-host": "node ./dist/scripts/update-host.js",
@@ -56,9 +51,9 @@
56 "regenerate-thumbnails": "node ./dist/scripts/regenerate-thumbnails.js", 51 "regenerate-thumbnails": "node ./dist/scripts/regenerate-thumbnails.js",
57 "create-import-video-file-job": "node ./dist/scripts/create-import-video-file-job.js", 52 "create-import-video-file-job": "node ./dist/scripts/create-import-video-file-job.js",
58 "print-transcode-command": "node ./dist/scripts/print-transcode-command.js", 53 "print-transcode-command": "node ./dist/scripts/print-transcode-command.js",
59 "test": "scripty", 54 "test": "sh ./scripts/test.sh",
60 "help": "scripty", 55 "help": "sh ./scripts/help.sh",
61 "generate-cli-doc": "scripty", 56 "generate-cli-doc": "sh ./scripts/generate-cli-doc.sh",
62 "parse-log": "node ./dist/scripts/parse-log.js", 57 "parse-log": "node ./dist/scripts/parse-log.js",
63 "prune-storage": "node ./dist/scripts/prune-storage.js", 58 "prune-storage": "node ./dist/scripts/prune-storage.js",
64 "optimize-old-videos": "node ./dist/scripts/optimize-old-videos.js", 59 "optimize-old-videos": "node ./dist/scripts/optimize-old-videos.js",
@@ -71,17 +66,14 @@
71 "ts-node": "ts-node", 66 "ts-node": "ts-node",
72 "eslint": "eslint", 67 "eslint": "eslint",
73 "concurrently": "concurrently", 68 "concurrently": "concurrently",
74 "sasslint": "sass-lint --verbose --no-exit",
75 "sasslint:fix": "sass-lint-auto-fix -c .sass-lint.yml --verbose",
76 "mocha": "mocha", 69 "mocha": "mocha",
77 "ci": "scripty", 70 "ci": "sh ./scripts/ci.sh",
78 "release": "scripty", 71 "release": "sh ./scripts/release.sh",
79 "release-embed-api": "scripty", 72 "release-embed-api": "sh ./scripts/release-embed-api.sh",
80 "nightly": "scripty", 73 "nightly": "sh ./scripts/nightly.sh",
81 "openapi-clients": "scripty", 74 "openapi-clients": "sh ./scripts/openapi-clients.sh",
82 "client-report": "scripty", 75 "client-report": "sh ./scripts/client-report.sh",
83 "swagger-cli": "swagger-cli", 76 "swagger-cli": "swagger-cli"
84 "sass-lint": "sass-lint"
85 }, 77 },
86 "dependencies": { 78 "dependencies": {
87 "apicache": "1.6.2", 79 "apicache": "1.6.2",
@@ -139,7 +131,6 @@
139 "redis": "^3.0.2", 131 "redis": "^3.0.2",
140 "reflect-metadata": "^0.1.12", 132 "reflect-metadata": "^0.1.12",
141 "sanitize-html": "2.x", 133 "sanitize-html": "2.x",
142 "scripty": "^2.0.0",
143 "sequelize": "6.6.2", 134 "sequelize": "6.6.2",
144 "sequelize-typescript": "^2.0.0-beta.1", 135 "sequelize-typescript": "^2.0.0-beta.1",
145 "sitemap": "^6.1.0", 136 "sitemap": "^6.1.0",
@@ -219,10 +210,6 @@
219 "ts-node": "9.1.1", 210 "ts-node": "9.1.1",
220 "typescript": "^4.0.5" 211 "typescript": "^4.0.5"
221 }, 212 },
222 "scripty": {
223 "silent": true
224 },
225 "sasslintConfig": "client/.sass-lint.yml",
226 "bundlewatch": { 213 "bundlewatch": {
227 "files": [ 214 "files": [
228 { 215 {
diff --git a/scripts/clean/client/dist.sh b/scripts/clean/client/index.sh
index 19b89de77..19b89de77 100755
--- a/scripts/clean/client/dist.sh
+++ b/scripts/clean/client/index.sh
diff --git a/scripts/clean/server/dist.sh b/scripts/clean/server/dist.sh
deleted file mode 100755
index 50722cb44..000000000
--- a/scripts/clean/server/dist.sh
+++ /dev/null
@@ -1,5 +0,0 @@
1#!/bin/sh
2
3set -eu
4
5rm -rf dist/
diff --git a/scripts/danger/clean/cleaner.ts b/scripts/danger/clean/cleaner.ts
deleted file mode 100644
index 69e8a63a0..000000000
--- a/scripts/danger/clean/cleaner.ts
+++ /dev/null
@@ -1,34 +0,0 @@
1import { registerTSPaths } from '../../../server/helpers/register-ts-paths'
2registerTSPaths()
3
4import * as Promise from 'bluebird'
5import * as rimraf from 'rimraf'
6import { initDatabaseModels, sequelizeTypescript } from '../../../server/initializers/database'
7import { CONFIG } from '../../../server/initializers/config'
8
9initDatabaseModels(true)
10 .then(() => {
11 return sequelizeTypescript.drop()
12 })
13 .then(() => {
14 console.info('Tables of %s deleted.', CONFIG.DATABASE.DBNAME)
15
16 const STORAGE = CONFIG.STORAGE
17 return Promise.mapSeries(Object.keys(STORAGE), storage => {
18 const storageDir = STORAGE[storage]
19
20 return new Promise((res, rej) => {
21 rimraf(storageDir, err => {
22 if (err) return rej(err)
23
24 console.info('%s deleted.', storageDir)
25 return res()
26 })
27 })
28 })
29 .then(() => process.exit(0))
30 })
31 .catch(err => {
32 console.error(err)
33 process.exit(-1)
34 })
diff --git a/scripts/danger/clean/dev.sh b/scripts/danger/clean/dev.sh
deleted file mode 100755
index 14b45e13b..000000000
--- a/scripts/danger/clean/dev.sh
+++ /dev/null
@@ -1,10 +0,0 @@
1#!/bin/bash
2
3set -eu
4
5read -p "This will remove all directories and SQL tables. Are you sure? (y/*) " -n 1 -r
6echo
7
8if [[ "$REPLY" =~ ^[Yy]$ ]]; then
9 NODE_ENV=test npm run ts-node -- --type-check "scripts/danger/clean/cleaner"
10fi
diff --git a/scripts/danger/clean/modules.sh b/scripts/danger/clean/modules.sh
deleted file mode 100755
index f59d6b675..000000000
--- a/scripts/danger/clean/modules.sh
+++ /dev/null
@@ -1,9 +0,0 @@
1#!/bin/bash
2
3set -eu
4
5read -p "This will remove all node server and client modules. Are you sure? " -n 1 -r
6
7if [[ "$REPLY" =~ ^[Yy]$ ]]; then
8 rm -rf node_modules client/node_modules
9fi
diff --git a/scripts/danger/clean/prod.sh b/scripts/danger/clean/prod.sh
deleted file mode 100755
index 0675600c4..000000000
--- a/scripts/danger/clean/prod.sh
+++ /dev/null
@@ -1,10 +0,0 @@
1#!/bin/bash
2
3set -eu
4
5read -p "This will remove all directories and SQL tables. Are you sure? (y/*) " -n 1 -r
6echo
7
8if [[ "$REPLY" =~ ^[Yy]$ ]]; then
9 NODE_ENV=production npm run ts-node -- --type-check "./scripts/danger/clean/cleaner"
10fi
diff --git a/scripts/help.sh b/scripts/help.sh
index bc38bdb40..da127092d 100755
--- a/scripts/help.sh
+++ b/scripts/help.sh
@@ -9,11 +9,6 @@ printf " build:server -> Build the server for production\n"
9printf " build:client -> Build the client for production\n" 9printf " build:client -> Build the client for production\n"
10printf " clean:client -> Clean the client build files (dist directory)\n" 10printf " clean:client -> Clean the client build files (dist directory)\n"
11printf " clean:server:test -> Clean logs, uploads, database... of the test instances\n" 11printf " clean:server:test -> Clean logs, uploads, database... of the test instances\n"
12printf " watch:client -> Watch and compile on the fly the client files\n"
13printf " danger:clean:dev -> /!\ Clean certificates, logs, uploads, thumbnails, torrents and database specified in the development environment\n"
14printf " danger:clean:prod -> /!\ Clean certificates, logs, uploads, thumbnails, torrents and database specified by the production environment\n"
15printf " danger:clean:modules -> /!\ Clean node and typescript modules\n"
16printf " play -> Run 3 fresh nodes so that you can test the communication between them\n"
17printf " reset-password -- -u [user] -> Reset the password of user [user]\n" 12printf " reset-password -- -u [user] -> Reset the password of user [user]\n"
18printf " create-transcoding-job -- -v [video UUID] \n" 13printf " create-transcoding-job -- -v [video UUID] \n"
19printf " -> Create a transcoding job for a particular video\n" 14printf " -> Create a transcoding job for a particular video\n"
diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts
index d4d5b44f0..b26e92d93 100755
--- a/scripts/i18n/create-custom-files.ts
+++ b/scripts/i18n/create-custom-files.ts
@@ -36,7 +36,22 @@ const playerKeys = {
36 'From servers: ': 'From servers: ', 36 'From servers: ': 'From servers: ',
37 'From peers: ': 'From peers: ', 37 'From peers: ': 'From peers: ',
38 'Normal mode': 'Normal mode', 38 'Normal mode': 'Normal mode',
39 'Theater mode': 'Theater mode' 39 'Stats for nerds': 'Stats for nerds',
40 'Theater mode': 'Theater mode',
41 'Video UUID': 'Video UUID',
42 'Viewport / Frames': 'Viewport / Frames',
43 'Resolution': 'Resolution',
44 'Volume': 'Volume',
45 'Codecs': 'Codecs',
46 'Color': 'Color',
47 'Connection Speed': 'Connection Speed',
48 'Network Activity': 'Network Activity',
49 'Total Transfered': 'Total Transfered',
50 'Download Breakdown': 'Download Breakdown',
51 'Buffer Progress': 'Buffer Progress',
52 'Buffer State': 'Buffer State',
53 'Live Latency': 'Live Latency',
54 'Player mode': 'Player mode'
40} 55}
41Object.assign(playerKeys, videojs) 56Object.assign(playerKeys, videojs)
42 57
diff --git a/scripts/play.sh b/scripts/play.sh
deleted file mode 100755
index 69725da93..000000000
--- a/scripts/play.sh
+++ /dev/null
@@ -1,15 +0,0 @@
1#!/bin/sh
2
3set -eu
4
5if [ ! -f "dist/server.js" ]; then
6 echo "Missing server file (server.js)."
7 exit -1
8fi
9
10max=${1:-3}
11
12for i in $(seq 1 "$max"); do
13 NODE_ENV=test NODE_APP_INSTANCE=$i node dist/server.js &
14 sleep 1
15done
diff --git a/scripts/prune-storage.ts b/scripts/prune-storage.ts
index bdfb335c6..32314b0b7 100755
--- a/scripts/prune-storage.ts
+++ b/scripts/prune-storage.ts
@@ -34,6 +34,8 @@ async function run () {
34 34
35 let toDelete: string[] = [] 35 let toDelete: string[] = []
36 36
37 console.log('Detecting files to remove, it could take a while...')
38
37 toDelete = toDelete.concat( 39 toDelete = toDelete.concat(
38 await pruneDirectory(CONFIG.STORAGE.VIDEOS_DIR, doesVideoExist(true)), 40 await pruneDirectory(CONFIG.STORAGE.VIDEOS_DIR, doesVideoExist(true)),
39 await pruneDirectory(CONFIG.STORAGE.TORRENTS_DIR, doesVideoExist(true)), 41 await pruneDirectory(CONFIG.STORAGE.TORRENTS_DIR, doesVideoExist(true)),
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index e31924a94..49a8e3195 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,9 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getServerActor } from '@server/models/application/application' 2import { getServerActor } from '@server/models/application/application'
3import { VideosWithSearchCommonQuery } from '@shared/models'
3import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 4import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
4import { getFormattedObjects } from '../../helpers/utils' 5import { getFormattedObjects } from '../../helpers/utils'
5import { Hooks } from '../../lib/plugins/hooks'
6import { JobQueue } from '../../lib/job-queue' 6import { JobQueue } from '../../lib/job-queue'
7import { Hooks } from '../../lib/plugins/hooks'
7import { 8import {
8 asyncMiddleware, 9 asyncMiddleware,
9 authenticate, 10 authenticate,
@@ -158,25 +159,27 @@ async function listAccountVideos (req: express.Request, res: express.Response) {
158 const account = res.locals.account 159 const account = res.locals.account
159 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined 160 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined
160 const countVideos = getCountVideos(req) 161 const countVideos = getCountVideos(req)
162 const query = req.query as VideosWithSearchCommonQuery
161 163
162 const apiOptions = await Hooks.wrapObject({ 164 const apiOptions = await Hooks.wrapObject({
163 followerActorId, 165 followerActorId,
164 start: req.query.start, 166 start: query.start,
165 count: req.query.count, 167 count: query.count,
166 sort: req.query.sort, 168 sort: query.sort,
167 includeLocalVideos: true, 169 includeLocalVideos: true,
168 categoryOneOf: req.query.categoryOneOf, 170 categoryOneOf: query.categoryOneOf,
169 licenceOneOf: req.query.licenceOneOf, 171 licenceOneOf: query.licenceOneOf,
170 languageOneOf: req.query.languageOneOf, 172 languageOneOf: query.languageOneOf,
171 tagsOneOf: req.query.tagsOneOf, 173 tagsOneOf: query.tagsOneOf,
172 tagsAllOf: req.query.tagsAllOf, 174 tagsAllOf: query.tagsAllOf,
173 filter: req.query.filter, 175 filter: query.filter,
174 nsfw: buildNSFWFilter(res, req.query.nsfw), 176 isLive: query.isLive,
177 nsfw: buildNSFWFilter(res, query.nsfw),
175 withFiles: false, 178 withFiles: false,
176 accountId: account.id, 179 accountId: account.id,
177 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 180 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
178 countVideos, 181 countVideos,
179 search: req.query.search 182 search: query.search
180 }, 'filter:api.accounts.videos.list.params') 183 }, 'filter:api.accounts.videos.list.params')
181 184
182 const resultList = await Hooks.wrapPromiseFun( 185 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index 9f9d2d77f..0763d1900 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -111,7 +111,8 @@ async function getUserVideos (req: express.Request, res: express.Response) {
111 start: req.query.start, 111 start: req.query.start,
112 count: req.query.count, 112 count: req.query.count,
113 sort: req.query.sort, 113 sort: req.query.sort,
114 search: req.query.search 114 search: req.query.search,
115 isLive: req.query.isLive
115 }, 'filter:api.user.me.videos.list.params') 116 }, 'filter:api.user.me.videos.list.params')
116 117
117 const resultList = await Hooks.wrapPromiseFun( 118 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts
index e8949ee59..56b93276f 100644
--- a/server/controllers/api/users/my-subscriptions.ts
+++ b/server/controllers/api/users/my-subscriptions.ts
@@ -2,8 +2,8 @@ import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { sendUndoFollow } from '@server/lib/activitypub/send' 3import { sendUndoFollow } from '@server/lib/activitypub/send'
4import { VideoChannelModel } from '@server/models/video/video-channel' 4import { VideoChannelModel } from '@server/models/video/video-channel'
5import { VideosCommonQuery } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
7import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' 7import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
8import { getFormattedObjects } from '../../../helpers/utils' 8import { getFormattedObjects } from '../../../helpers/utils'
9import { WEBSERVER } from '../../../initializers/constants' 9import { WEBSERVER } from '../../../initializers/constants'
@@ -170,19 +170,20 @@ async function getUserSubscriptions (req: express.Request, res: express.Response
170async function getUserSubscriptionVideos (req: express.Request, res: express.Response) { 170async function getUserSubscriptionVideos (req: express.Request, res: express.Response) {
171 const user = res.locals.oauth.token.User 171 const user = res.locals.oauth.token.User
172 const countVideos = getCountVideos(req) 172 const countVideos = getCountVideos(req)
173 const query = req.query as VideosCommonQuery
173 174
174 const resultList = await VideoModel.listForApi({ 175 const resultList = await VideoModel.listForApi({
175 start: req.query.start, 176 start: query.start,
176 count: req.query.count, 177 count: query.count,
177 sort: req.query.sort, 178 sort: query.sort,
178 includeLocalVideos: false, 179 includeLocalVideos: false,
179 categoryOneOf: req.query.categoryOneOf, 180 categoryOneOf: query.categoryOneOf,
180 licenceOneOf: req.query.licenceOneOf, 181 licenceOneOf: query.licenceOneOf,
181 languageOneOf: req.query.languageOneOf, 182 languageOneOf: query.languageOneOf,
182 tagsOneOf: req.query.tagsOneOf, 183 tagsOneOf: query.tagsOneOf,
183 tagsAllOf: req.query.tagsAllOf, 184 tagsAllOf: query.tagsAllOf,
184 nsfw: buildNSFWFilter(res, req.query.nsfw), 185 nsfw: buildNSFWFilter(res, query.nsfw),
185 filter: req.query.filter as VideoFilter, 186 filter: query.filter,
186 withFiles: false, 187 withFiles: false,
187 followerActorId: user.Account.Actor.id, 188 followerActorId: user.Account.Actor.id,
188 user, 189 user,
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 149d6cfb4..a755d7e57 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { Hooks } from '@server/lib/plugins/hooks' 2import { Hooks } from '@server/lib/plugins/hooks'
3import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
4import { MChannelBannerAccountDefault } from '@server/types/models' 4import { MChannelBannerAccountDefault } from '@server/types/models'
5import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared' 5import { ActorImageType, VideoChannelCreate, VideoChannelUpdate, VideosCommonQuery } from '../../../shared'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' 7import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger'
8import { resetSequelizeInstance } from '../../helpers/database-utils' 8import { resetSequelizeInstance } from '../../helpers/database-utils'
@@ -312,20 +312,21 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
312 const videoChannelInstance = res.locals.videoChannel 312 const videoChannelInstance = res.locals.videoChannel
313 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined 313 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined
314 const countVideos = getCountVideos(req) 314 const countVideos = getCountVideos(req)
315 const query = req.query as VideosCommonQuery
315 316
316 const apiOptions = await Hooks.wrapObject({ 317 const apiOptions = await Hooks.wrapObject({
317 followerActorId, 318 followerActorId,
318 start: req.query.start, 319 start: query.start,
319 count: req.query.count, 320 count: query.count,
320 sort: req.query.sort, 321 sort: query.sort,
321 includeLocalVideos: true, 322 includeLocalVideos: true,
322 categoryOneOf: req.query.categoryOneOf, 323 categoryOneOf: query.categoryOneOf,
323 licenceOneOf: req.query.licenceOneOf, 324 licenceOneOf: query.licenceOneOf,
324 languageOneOf: req.query.languageOneOf, 325 languageOneOf: query.languageOneOf,
325 tagsOneOf: req.query.tagsOneOf, 326 tagsOneOf: query.tagsOneOf,
326 tagsAllOf: req.query.tagsAllOf, 327 tagsAllOf: query.tagsAllOf,
327 filter: req.query.filter, 328 filter: query.filter,
328 nsfw: buildNSFWFilter(res, req.query.nsfw), 329 nsfw: buildNSFWFilter(res, query.nsfw),
329 withFiles: false, 330 withFiles: false,
330 videoChannelId: videoChannelInstance.id, 331 videoChannelId: videoChannelInstance.id,
331 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 332 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 7fee278f2..6ec6478e4 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -10,9 +10,8 @@ import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnail
10import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 10import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
11import { getServerActor } from '@server/models/application/application' 11import { getServerActor } from '@server/models/application/application'
12import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 12import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
13import { VideoCreate, VideoState, VideoUpdate } from '../../../../shared' 13import { VideoCreate, VideosCommonQuery, VideoState, VideoUpdate } from '../../../../shared'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
16import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 15import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
17import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' 16import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils'
18import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' 17import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils'
@@ -494,20 +493,22 @@ async function getVideoFileMetadata (req: express.Request, res: express.Response
494} 493}
495 494
496async function listVideos (req: express.Request, res: express.Response) { 495async function listVideos (req: express.Request, res: express.Response) {
496 const query = req.query as VideosCommonQuery
497 const countVideos = getCountVideos(req) 497 const countVideos = getCountVideos(req)
498 498
499 const apiOptions = await Hooks.wrapObject({ 499 const apiOptions = await Hooks.wrapObject({
500 start: req.query.start, 500 start: query.start,
501 count: req.query.count, 501 count: query.count,
502 sort: req.query.sort, 502 sort: query.sort,
503 includeLocalVideos: true, 503 includeLocalVideos: true,
504 categoryOneOf: req.query.categoryOneOf, 504 categoryOneOf: query.categoryOneOf,
505 licenceOneOf: req.query.licenceOneOf, 505 licenceOneOf: query.licenceOneOf,
506 languageOneOf: req.query.languageOneOf, 506 languageOneOf: query.languageOneOf,
507 tagsOneOf: req.query.tagsOneOf, 507 tagsOneOf: query.tagsOneOf,
508 tagsAllOf: req.query.tagsAllOf, 508 tagsAllOf: query.tagsAllOf,
509 nsfw: buildNSFWFilter(res, req.query.nsfw), 509 nsfw: buildNSFWFilter(res, query.nsfw),
510 filter: req.query.filter as VideoFilter, 510 isLive: query.isLive,
511 filter: query.filter,
511 withFiles: false, 512 withFiles: false,
512 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 513 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
513 countVideos 514 countVideos
diff --git a/server/helpers/custom-validators/search.ts b/server/helpers/custom-validators/search.ts
index 429fcafcf..a8f258838 100644
--- a/server/helpers/custom-validators/search.ts
+++ b/server/helpers/custom-validators/search.ts
@@ -11,7 +11,7 @@ function isStringArray (value: any) {
11 return isArray(value) && value.every(v => typeof v === 'string') 11 return isArray(value) && value.every(v => typeof v === 'string')
12} 12}
13 13
14function isNSFWQueryValid (value: any) { 14function isBooleanBothQueryValid (value: any) {
15 return value === 'true' || value === 'false' || value === 'both' 15 return value === 'true' || value === 'false' || value === 'both'
16} 16}
17 17
@@ -32,6 +32,6 @@ function isSearchTargetValid (value: SearchTargetType) {
32export { 32export {
33 isNumberArray, 33 isNumberArray,
34 isStringArray, 34 isStringArray,
35 isNSFWQueryValid, 35 isBooleanBothQueryValid,
36 isSearchTargetValid 36 isSearchTargetValid
37} 37}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 37a963760..d390fd95e 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -188,10 +188,7 @@ const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } =
188 } 188 }
189} 189}
190const JOB_PRIORITY = { 190const JOB_PRIORITY = {
191 TRANSCODING: { 191 TRANSCODING: 100
192 OPTIMIZER: 10,
193 NEW_RESOLUTION: 100
194 }
195} 192}
196 193
197const BROADCAST_CONCURRENCY = 30 // How many requests in parallel we do in activitypub-http-broadcast job 194const BROADCAST_CONCURRENCY = 30 // How many requests in parallel we do in activitypub-http-broadcast job
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index 4ee2b2df2..010b95b05 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -1,7 +1,6 @@
1import * as Bull from 'bull' 1import * as Bull from 'bull'
2import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' 2import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils'
3import { JOB_PRIORITY } from '@server/initializers/constants' 3import { getTranscodingJobPriority, publishAndFederateIfNeeded } from '@server/lib/video'
4import { getJobTranscodingPriorityMalus, publishAndFederateIfNeeded } from '@server/lib/video'
5import { getVideoFilePath } from '@server/lib/video-paths' 4import { getVideoFilePath } from '@server/lib/video-paths'
6import { UserModel } from '@server/models/account/user' 5import { UserModel } from '@server/models/account/user'
7import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' 6import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models'
@@ -215,7 +214,7 @@ async function createHlsJobIfEnabled (user: MUserId, payload: {
215 if (!payload || CONFIG.TRANSCODING.HLS.ENABLED !== true) return false 214 if (!payload || CONFIG.TRANSCODING.HLS.ENABLED !== true) return false
216 215
217 const jobOptions = { 216 const jobOptions = {
218 priority: JOB_PRIORITY.TRANSCODING.NEW_RESOLUTION + await getJobTranscodingPriorityMalus(user) 217 priority: await getTranscodingJobPriority(user)
219 } 218 }
220 219
221 const hlsTranscodingPayload: HLSTranscodingPayload = { 220 const hlsTranscodingPayload: HLSTranscodingPayload = {
@@ -272,7 +271,7 @@ async function createLowerResolutionsJobs (
272 resolutionCreated.push(resolution) 271 resolutionCreated.push(resolution)
273 272
274 const jobOptions = { 273 const jobOptions = {
275 priority: JOB_PRIORITY.TRANSCODING.NEW_RESOLUTION + await getJobTranscodingPriorityMalus(user) 274 priority: await getTranscodingJobPriority(user)
276 } 275 }
277 276
278 JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }, jobOptions) 277 JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }, jobOptions)
diff --git a/server/lib/video.ts b/server/lib/video.ts
index e381e0a69..9469b8178 100644
--- a/server/lib/video.ts
+++ b/server/lib/video.ts
@@ -121,19 +121,19 @@ async function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile,
121 } 121 }
122 122
123 const jobOptions = { 123 const jobOptions = {
124 priority: JOB_PRIORITY.TRANSCODING.OPTIMIZER + await getJobTranscodingPriorityMalus(user) 124 priority: await getTranscodingJobPriority(user)
125 } 125 }
126 126
127 return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput }, jobOptions) 127 return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput }, jobOptions)
128} 128}
129 129
130async function getJobTranscodingPriorityMalus (user: MUserId) { 130async function getTranscodingJobPriority (user: MUserId) {
131 const now = new Date() 131 const now = new Date()
132 const lastWeek = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7) 132 const lastWeek = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7)
133 133
134 const videoUploadedByUser = await VideoModel.countVideosUploadedByUserSince(user.id, lastWeek) 134 const videoUploadedByUser = await VideoModel.countVideosUploadedByUserSince(user.id, lastWeek)
135 135
136 return videoUploadedByUser 136 return JOB_PRIORITY.TRANSCODING + videoUploadedByUser
137} 137}
138 138
139// --------------------------------------------------------------------------- 139// ---------------------------------------------------------------------------
@@ -144,5 +144,5 @@ export {
144 buildVideoThumbnailsFromReq, 144 buildVideoThumbnailsFromReq,
145 setVideoTags, 145 setVideoTags,
146 addOptimizeOrMergeAudioJob, 146 addOptimizeOrMergeAudioJob,
147 getJobTranscodingPriorityMalus 147 getTranscodingJobPriority
148} 148}
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 4d31d3dcb..bb617d77c 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -20,7 +20,7 @@ import {
20 toIntOrNull, 20 toIntOrNull,
21 toValueOrNull 21 toValueOrNull
22} from '../../../helpers/custom-validators/misc' 22} from '../../../helpers/custom-validators/misc'
23import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' 23import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
24import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' 24import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership'
25import { 25import {
26 isScheduleVideoUpdatePrivacyValid, 26 isScheduleVideoUpdatePrivacyValid,
@@ -439,7 +439,11 @@ const commonVideosFiltersValidator = [
439 .custom(isStringArray).withMessage('Should have a valid all of tags array'), 439 .custom(isStringArray).withMessage('Should have a valid all of tags array'),
440 query('nsfw') 440 query('nsfw')
441 .optional() 441 .optional()
442 .custom(isNSFWQueryValid).withMessage('Should have a valid NSFW attribute'), 442 .custom(isBooleanBothQueryValid).withMessage('Should have a valid NSFW attribute'),
443 query('isLive')
444 .optional()
445 .customSanitizer(toBooleanOrNull)
446 .custom(isBooleanValid).withMessage('Should have a valid live boolean'),
443 query('filter') 447 query('filter')
444 .optional() 448 .optional()
445 .custom(isVideoFilterValid).withMessage('Should have a valid filter attribute'), 449 .custom(isVideoFilterValid).withMessage('Should have a valid filter attribute'),
diff --git a/server/models/video/video-query-builder.ts b/server/models/video/video-query-builder.ts
index 4d95ddee2..155afe64b 100644
--- a/server/models/video/video-query-builder.ts
+++ b/server/models/video/video-query-builder.ts
@@ -16,9 +16,11 @@ export type BuildVideosQueryOptions = {
16 start: number 16 start: number
17 sort: string 17 sort: string
18 18
19 nsfw?: boolean
19 filter?: VideoFilter 20 filter?: VideoFilter
21 isLive?: boolean
22
20 categoryOneOf?: number[] 23 categoryOneOf?: number[]
21 nsfw?: boolean
22 licenceOneOf?: number[] 24 licenceOneOf?: number[]
23 languageOneOf?: string[] 25 languageOneOf?: string[]
24 tagsOneOf?: string[] 26 tagsOneOf?: string[]
@@ -199,10 +201,14 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
199 201
200 if (options.nsfw === true) { 202 if (options.nsfw === true) {
201 and.push('"video"."nsfw" IS TRUE') 203 and.push('"video"."nsfw" IS TRUE')
204 } else if (options.nsfw === false) {
205 and.push('"video"."nsfw" IS FALSE')
202 } 206 }
203 207
204 if (options.nsfw === false) { 208 if (options.isLive === true) {
205 and.push('"video"."nsfw" IS FALSE') 209 and.push('"video"."isLive" IS TRUE')
210 } else if (options.isLive === false) {
211 and.push('"video"."isLive" IS FALSE')
206 } 212 }
207 213
208 if (options.categoryOneOf) { 214 if (options.categoryOneOf) {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 422bf6deb..e55a21a6b 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1021,14 +1021,28 @@ export class VideoModel extends Model {
1021 start: number 1021 start: number
1022 count: number 1022 count: number
1023 sort: string 1023 sort: string
1024 isLive?: boolean
1024 search?: string 1025 search?: string
1025 }) { 1026 }) {
1026 const { accountId, start, count, sort, search } = options 1027 const { accountId, start, count, sort, search, isLive } = options
1027 1028
1028 function buildBaseQuery (): FindOptions { 1029 function buildBaseQuery (): FindOptions {
1029 let baseQuery = { 1030 const where: WhereOptions = {}
1031
1032 if (search) {
1033 where.name = {
1034 [Op.iLike]: '%' + search + '%'
1035 }
1036 }
1037
1038 if (isLive) {
1039 where.isLive = isLive
1040 }
1041
1042 const baseQuery = {
1030 offset: start, 1043 offset: start,
1031 limit: count, 1044 limit: count,
1045 where,
1032 order: getVideoSort(sort), 1046 order: getVideoSort(sort),
1033 include: [ 1047 include: [
1034 { 1048 {
@@ -1047,16 +1061,6 @@ export class VideoModel extends Model {
1047 ] 1061 ]
1048 } 1062 }
1049 1063
1050 if (search) {
1051 baseQuery = Object.assign(baseQuery, {
1052 where: {
1053 name: {
1054 [Op.iLike]: '%' + search + '%'
1055 }
1056 }
1057 })
1058 }
1059
1060 return baseQuery 1064 return baseQuery
1061 } 1065 }
1062 1066
@@ -1084,23 +1088,34 @@ export class VideoModel extends Model {
1084 start: number 1088 start: number
1085 count: number 1089 count: number
1086 sort: string 1090 sort: string
1091
1087 nsfw: boolean 1092 nsfw: boolean
1093 filter?: VideoFilter
1094 isLive?: boolean
1095
1088 includeLocalVideos: boolean 1096 includeLocalVideos: boolean
1089 withFiles: boolean 1097 withFiles: boolean
1098
1090 categoryOneOf?: number[] 1099 categoryOneOf?: number[]
1091 licenceOneOf?: number[] 1100 licenceOneOf?: number[]
1092 languageOneOf?: string[] 1101 languageOneOf?: string[]
1093 tagsOneOf?: string[] 1102 tagsOneOf?: string[]
1094 tagsAllOf?: string[] 1103 tagsAllOf?: string[]
1095 filter?: VideoFilter 1104
1096 accountId?: number 1105 accountId?: number
1097 videoChannelId?: number 1106 videoChannelId?: number
1107
1098 followerActorId?: number 1108 followerActorId?: number
1109
1099 videoPlaylistId?: number 1110 videoPlaylistId?: number
1111
1100 trendingDays?: number 1112 trendingDays?: number
1113
1101 user?: MUserAccountId 1114 user?: MUserAccountId
1102 historyOfUser?: MUserId 1115 historyOfUser?: MUserId
1116
1103 countVideos?: boolean 1117 countVideos?: boolean
1118
1104 search?: string 1119 search?: string
1105 }) { 1120 }) {
1106 if ((options.filter === 'all-local' || options.filter === 'all') && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { 1121 if ((options.filter === 'all-local' || options.filter === 'all') && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
@@ -1128,6 +1143,7 @@ export class VideoModel extends Model {
1128 followerActorId, 1143 followerActorId,
1129 serverAccountId: serverActor.Account.id, 1144 serverAccountId: serverActor.Account.id,
1130 nsfw: options.nsfw, 1145 nsfw: options.nsfw,
1146 isLive: options.isLive,
1131 categoryOneOf: options.categoryOneOf, 1147 categoryOneOf: options.categoryOneOf,
1132 licenceOneOf: options.licenceOneOf, 1148 licenceOneOf: options.licenceOneOf,
1133 languageOneOf: options.languageOneOf, 1149 languageOneOf: options.languageOneOf,
@@ -1160,6 +1176,7 @@ export class VideoModel extends Model {
1160 originallyPublishedStartDate?: string 1176 originallyPublishedStartDate?: string
1161 originallyPublishedEndDate?: string 1177 originallyPublishedEndDate?: string
1162 nsfw?: boolean 1178 nsfw?: boolean
1179 isLive?: boolean
1163 categoryOneOf?: number[] 1180 categoryOneOf?: number[]
1164 licenceOneOf?: number[] 1181 licenceOneOf?: number[]
1165 languageOneOf?: string[] 1182 languageOneOf?: string[]
@@ -1171,23 +1188,32 @@ export class VideoModel extends Model {
1171 filter?: VideoFilter 1188 filter?: VideoFilter
1172 }) { 1189 }) {
1173 const serverActor = await getServerActor() 1190 const serverActor = await getServerActor()
1191
1174 const queryOptions = { 1192 const queryOptions = {
1175 followerActorId: serverActor.id, 1193 followerActorId: serverActor.id,
1176 serverAccountId: serverActor.Account.id, 1194 serverAccountId: serverActor.Account.id,
1195
1177 includeLocalVideos: options.includeLocalVideos, 1196 includeLocalVideos: options.includeLocalVideos,
1178 nsfw: options.nsfw, 1197 nsfw: options.nsfw,
1198 isLive: options.isLive,
1199
1179 categoryOneOf: options.categoryOneOf, 1200 categoryOneOf: options.categoryOneOf,
1180 licenceOneOf: options.licenceOneOf, 1201 licenceOneOf: options.licenceOneOf,
1181 languageOneOf: options.languageOneOf, 1202 languageOneOf: options.languageOneOf,
1203
1182 tagsOneOf: options.tagsOneOf, 1204 tagsOneOf: options.tagsOneOf,
1183 tagsAllOf: options.tagsAllOf, 1205 tagsAllOf: options.tagsAllOf,
1206
1184 user: options.user, 1207 user: options.user,
1185 filter: options.filter, 1208 filter: options.filter,
1209
1186 start: options.start, 1210 start: options.start,
1187 count: options.count, 1211 count: options.count,
1188 sort: options.sort, 1212 sort: options.sort,
1213
1189 startDate: options.startDate, 1214 startDate: options.startDate,
1190 endDate: options.endDate, 1215 endDate: options.endDate,
1216
1191 originallyPublishedStartDate: options.originallyPublishedStartDate, 1217 originallyPublishedStartDate: options.originallyPublishedStartDate,
1192 originallyPublishedEndDate: options.originallyPublishedEndDate, 1218 originallyPublishedEndDate: options.originallyPublishedEndDate,
1193 1219
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index d48e2a8ee..57fb58150 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -19,10 +19,12 @@ import {
19 doubleFollow, 19 doubleFollow,
20 flushAndRunMultipleServers, 20 flushAndRunMultipleServers,
21 getLive, 21 getLive,
22 getMyVideosWithFilter,
22 getPlaylist, 23 getPlaylist,
23 getVideo, 24 getVideo,
24 getVideoIdFromUUID, 25 getVideoIdFromUUID,
25 getVideosList, 26 getVideosList,
27 getVideosWithFilters,
26 killallServers, 28 killallServers,
27 makeRawRequest, 29 makeRawRequest,
28 removeVideo, 30 removeVideo,
@@ -37,6 +39,7 @@ import {
37 testImage, 39 testImage,
38 updateCustomSubConfig, 40 updateCustomSubConfig,
39 updateLive, 41 updateLive,
42 uploadVideoAndGetId,
40 viewVideo, 43 viewVideo,
41 wait, 44 wait,
42 waitJobs, 45 waitJobs,
@@ -229,6 +232,68 @@ describe('Test live', function () {
229 }) 232 })
230 }) 233 })
231 234
235 describe('Live filters', function () {
236 let command: any
237 let liveVideoId: string
238 let vodVideoId: string
239
240 before(async function () {
241 this.timeout(120000)
242
243 vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid
244
245 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id }
246 const resLive = await createLive(servers[0].url, servers[0].accessToken, liveOptions)
247 liveVideoId = resLive.body.video.uuid
248
249 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
250 await waitUntilLivePublishedOnAllServers(liveVideoId)
251 await waitJobs(servers)
252 })
253
254 it('Should only display lives', async function () {
255 const res = await getVideosWithFilters(servers[0].url, { isLive: true })
256
257 expect(res.body.total).to.equal(1)
258 expect(res.body.data).to.have.lengthOf(1)
259 expect(res.body.data[0].name).to.equal('live')
260 })
261
262 it('Should not display lives', async function () {
263 const res = await getVideosWithFilters(servers[0].url, { isLive: false })
264
265 expect(res.body.total).to.equal(1)
266 expect(res.body.data).to.have.lengthOf(1)
267 expect(res.body.data[0].name).to.equal('vod video')
268 })
269
270 it('Should display my lives', async function () {
271 this.timeout(60000)
272
273 await stopFfmpeg(command)
274 await waitJobs(servers)
275
276 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true })
277 const videos = res.body.data as Video[]
278
279 const result = videos.every(v => v.isLive)
280 expect(result).to.be.true
281 })
282
283 it('Should not display my lives', async function () {
284 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: false })
285 const videos = res.body.data as Video[]
286
287 const result = videos.every(v => !v.isLive)
288 expect(result).to.be.true
289 })
290
291 after(async function () {
292 await removeVideo(servers[0].url, servers[0].accessToken, vodVideoId)
293 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoId)
294 })
295 })
296
232 describe('Stream checks', function () { 297 describe('Stream checks', function () {
233 let liveVideo: LiveVideo & VideoDetails 298 let liveVideo: LiveVideo & VideoDetails
234 let rtmpUrl: string 299 let rtmpUrl: string
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts
index e05c3a269..5b8907961 100644
--- a/server/tests/api/search/search-videos.ts
+++ b/server/tests/api/search/search-videos.ts
@@ -1,17 +1,24 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { VideoPrivacy } from '@shared/models'
5import { 6import {
6 advancedVideosSearch, 7 advancedVideosSearch,
7 cleanupTests, 8 cleanupTests,
9 createLive,
8 flushAndRunServer, 10 flushAndRunServer,
9 immutableAssign, 11 immutableAssign,
10 searchVideo, 12 searchVideo,
13 sendRTMPStreamInVideo,
11 ServerInfo, 14 ServerInfo,
12 setAccessTokensToServers, 15 setAccessTokensToServers,
16 setDefaultVideoChannel,
17 stopFfmpeg,
18 updateCustomSubConfig,
13 uploadVideo, 19 uploadVideo,
14 wait 20 wait,
21 waitUntilLivePublished
15} from '../../../../shared/extra-utils' 22} from '../../../../shared/extra-utils'
16import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions' 23import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
17 24
@@ -28,6 +35,7 @@ describe('Test videos search', function () {
28 server = await flushAndRunServer(1) 35 server = await flushAndRunServer(1)
29 36
30 await setAccessTokensToServers([ server ]) 37 await setAccessTokensToServers([ server ])
38 await setDefaultVideoChannel([ server ])
31 39
32 { 40 {
33 const attributes1 = { 41 const attributes1 = {
@@ -449,6 +457,43 @@ describe('Test videos search', function () {
449 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 457 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3')
450 }) 458 })
451 459
460 it('Should search by live', async function () {
461 this.timeout(30000)
462
463 {
464 const options = {
465 search: {
466 searchIndex: { enabled: false }
467 },
468 live: { enabled: true }
469 }
470 await updateCustomSubConfig(server.url, server.accessToken, options)
471 }
472
473 {
474 const res = await advancedVideosSearch(server.url, { isLive: true })
475
476 expect(res.body.total).to.equal(0)
477 expect(res.body.data).to.have.lengthOf(0)
478 }
479
480 {
481 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id }
482 const resLive = await createLive(server.url, server.accessToken, liveOptions)
483 const liveVideoId = resLive.body.video.uuid
484
485 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId)
486 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
487
488 const res = await advancedVideosSearch(server.url, { isLive: true })
489
490 expect(res.body.total).to.equal(1)
491 expect(res.body.data[0].name).to.equal('live')
492
493 await stopFfmpeg(command)
494 }
495 })
496
452 after(async function () { 497 after(async function () {
453 await cleanupTests([ server ]) 498 await cleanupTests([ server ])
454 }) 499 })
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index da90223b8..a79648bf7 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -387,11 +387,11 @@ describe('Test a single server', function () {
387 }) 387 })
388 388
389 it('Should filter by tags and category', async function () { 389 it('Should filter by tags and category', async function () {
390 const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: 4 }) 390 const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] })
391 expect(res1.body.total).to.equal(1) 391 expect(res1.body.total).to.equal(1)
392 expect(res1.body.data[0].name).to.equal('my super video updated') 392 expect(res1.body.data[0].name).to.equal('my super video updated')
393 393
394 const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: 3 }) 394 const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] })
395 expect(res2.body.total).to.equal(0) 395 expect(res2.body.total).to.equal(0)
396 }) 396 })
397 397
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts
index 1058baaa3..1c99f26df 100644
--- a/server/tests/api/videos/video-transcoder.ts
+++ b/server/tests/api/videos/video-transcoder.ts
@@ -721,12 +721,7 @@ describe('Test video transcoding', function () {
721 expect(webtorrentJobs).to.have.lengthOf(6) 721 expect(webtorrentJobs).to.have.lengthOf(6)
722 expect(optimizeJobs).to.have.lengthOf(1) 722 expect(optimizeJobs).to.have.lengthOf(1)
723 723
724 for (const j of optimizeJobs) { 724 for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) {
725 expect(j.priority).to.be.greaterThan(11)
726 expect(j.priority).to.be.lessThan(50)
727 }
728
729 for (const j of hlsJobs.concat(webtorrentJobs)) {
730 expect(j.priority).to.be.greaterThan(100) 725 expect(j.priority).to.be.greaterThan(100)
731 expect(j.priority).to.be.lessThan(150) 726 expect(j.priority).to.be.lessThan(150)
732 } 727 }
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index 67fe82d41..a0143b0ef 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -8,6 +8,7 @@ import * as request from 'supertest'
8import { v4 as uuidv4 } from 'uuid' 8import { v4 as uuidv4 } from 'uuid'
9import validator from 'validator' 9import validator from 'validator'
10import { HttpStatusCode } from '@shared/core-utils' 10import { HttpStatusCode } from '@shared/core-utils'
11import { VideosCommonQuery } from '@shared/models'
11import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' 12import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
12import { VideoDetails, VideoPrivacy } from '../../models/videos' 13import { VideoDetails, VideoPrivacy } from '../../models/videos'
13import { 14import {
@@ -195,6 +196,18 @@ function getMyVideos (url: string, accessToken: string, start: number, count: nu
195 .expect('Content-Type', /json/) 196 .expect('Content-Type', /json/)
196} 197}
197 198
199function getMyVideosWithFilter (url: string, accessToken: string, query: { isLive?: boolean }) {
200 const path = '/api/v1/users/me/videos'
201
202 return makeGetRequest({
203 url,
204 path,
205 token: accessToken,
206 query,
207 statusCodeExpected: HttpStatusCode.OK_200
208 })
209}
210
198function getAccountVideos ( 211function getAccountVideos (
199 url: string, 212 url: string,
200 accessToken: string, 213 accessToken: string,
@@ -295,7 +308,7 @@ function getVideosListSort (url: string, sort: string) {
295 .expect('Content-Type', /json/) 308 .expect('Content-Type', /json/)
296} 309}
297 310
298function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) { 311function getVideosWithFilters (url: string, query: VideosCommonQuery) {
299 const path = '/api/v1/videos' 312 const path = '/api/v1/videos'
300 313
301 return request(url) 314 return request(url)
@@ -751,6 +764,7 @@ export {
751 completeVideoCheck, 764 completeVideoCheck,
752 checkVideoFilesWereRemoved, 765 checkVideoFilesWereRemoved,
753 getPlaylistVideos, 766 getPlaylistVideos,
767 getMyVideosWithFilter,
754 uploadVideoAndGetId, 768 uploadVideoAndGetId,
755 getLocalIdByUUID, 769 getLocalIdByUUID,
756 getVideoIdFromUUID 770 getVideoIdFromUUID
diff --git a/shared/models/search/boolean-both-query.model.ts b/shared/models/search/boolean-both-query.model.ts
new file mode 100644
index 000000000..57b0e8d44
--- /dev/null
+++ b/shared/models/search/boolean-both-query.model.ts
@@ -0,0 +1 @@
export type BooleanBothQuery = 'true' | 'false' | 'both'
diff --git a/shared/models/search/index.ts b/shared/models/search/index.ts
index e2d0ab620..697ceccb1 100644
--- a/shared/models/search/index.ts
+++ b/shared/models/search/index.ts
@@ -1,4 +1,5 @@
1export * from './nsfw-query.model' 1export * from './boolean-both-query.model'
2export * from './search-target-query.model' 2export * from './search-target-query.model'
3export * from './videos-common-query.model'
3export * from './videos-search-query.model' 4export * from './videos-search-query.model'
4export * from './video-channels-search-query.model' 5export * from './video-channels-search-query.model'
diff --git a/shared/models/search/nsfw-query.model.ts b/shared/models/search/nsfw-query.model.ts
deleted file mode 100644
index 6b6ad1991..000000000
--- a/shared/models/search/nsfw-query.model.ts
+++ /dev/null
@@ -1 +0,0 @@
1export type NSFWQuery = 'true' | 'false' | 'both'
diff --git a/shared/models/search/videos-common-query.model.ts b/shared/models/search/videos-common-query.model.ts
new file mode 100644
index 000000000..bd02489ea
--- /dev/null
+++ b/shared/models/search/videos-common-query.model.ts
@@ -0,0 +1,28 @@
1import { VideoFilter } from '../videos'
2import { BooleanBothQuery } from './boolean-both-query.model'
3
4// These query parameters can be used with any endpoint that list videos
5export interface VideosCommonQuery {
6 start?: number
7 count?: number
8 sort?: string
9
10 nsfw?: BooleanBothQuery
11
12 isLive?: boolean
13
14 categoryOneOf?: number[]
15
16 licenceOneOf?: number[]
17
18 languageOneOf?: string[]
19
20 tagsOneOf?: string[]
21 tagsAllOf?: string[]
22
23 filter?: VideoFilter
24}
25
26export interface VideosWithSearchCommonQuery extends VideosCommonQuery {
27 search?: string
28}
diff --git a/shared/models/search/videos-search-query.model.ts b/shared/models/search/videos-search-query.model.ts
index 3ce4ff73e..406f6cab2 100644
--- a/shared/models/search/videos-search-query.model.ts
+++ b/shared/models/search/videos-search-query.model.ts
@@ -1,33 +1,15 @@
1import { VideoFilter } from '../videos'
2import { NSFWQuery } from './nsfw-query.model'
3import { SearchTargetQuery } from './search-target-query.model' 1import { SearchTargetQuery } from './search-target-query.model'
2import { VideosCommonQuery } from './videos-common-query.model'
4 3
5export interface VideosSearchQuery extends SearchTargetQuery { 4export interface VideosSearchQuery extends SearchTargetQuery, VideosCommonQuery {
6 search?: string 5 search?: string
7 6
8 start?: number
9 count?: number
10 sort?: string
11
12 startDate?: string // ISO 8601 7 startDate?: string // ISO 8601
13 endDate?: string // ISO 8601 8 endDate?: string // ISO 8601
14 9
15 originallyPublishedStartDate?: string // ISO 8601 10 originallyPublishedStartDate?: string // ISO 8601
16 originallyPublishedEndDate?: string // ISO 8601 11 originallyPublishedEndDate?: string // ISO 8601
17 12
18 nsfw?: NSFWQuery
19
20 categoryOneOf?: number[]
21
22 licenceOneOf?: number[]
23
24 languageOneOf?: string[]
25
26 tagsOneOf?: string[]
27 tagsAllOf?: string[]
28
29 durationMin?: number // seconds 13 durationMin?: number // seconds
30 durationMax?: number // seconds 14 durationMax?: number // seconds
31
32 filter?: VideoFilter
33} 15}
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index a63ee7983..d4fe15664 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -210,6 +210,7 @@ paths:
210 parameters: 210 parameters:
211 - $ref: '#/components/parameters/name' 211 - $ref: '#/components/parameters/name'
212 - $ref: '#/components/parameters/categoryOneOf' 212 - $ref: '#/components/parameters/categoryOneOf'
213 - $ref: '#/components/parameters/isLive'
213 - $ref: '#/components/parameters/tagsOneOf' 214 - $ref: '#/components/parameters/tagsOneOf'
214 - $ref: '#/components/parameters/tagsAllOf' 215 - $ref: '#/components/parameters/tagsAllOf'
215 - $ref: '#/components/parameters/licenceOneOf' 216 - $ref: '#/components/parameters/licenceOneOf'
@@ -781,6 +782,7 @@ paths:
781 - Videos 782 - Videos
782 parameters: 783 parameters:
783 - $ref: '#/components/parameters/categoryOneOf' 784 - $ref: '#/components/parameters/categoryOneOf'
785 - $ref: '#/components/parameters/isLive'
784 - $ref: '#/components/parameters/tagsOneOf' 786 - $ref: '#/components/parameters/tagsOneOf'
785 - $ref: '#/components/parameters/tagsAllOf' 787 - $ref: '#/components/parameters/tagsAllOf'
786 - $ref: '#/components/parameters/licenceOneOf' 788 - $ref: '#/components/parameters/licenceOneOf'
@@ -1086,6 +1088,7 @@ paths:
1086 - Video 1088 - Video
1087 parameters: 1089 parameters:
1088 - $ref: '#/components/parameters/categoryOneOf' 1090 - $ref: '#/components/parameters/categoryOneOf'
1091 - $ref: '#/components/parameters/isLive'
1089 - $ref: '#/components/parameters/tagsOneOf' 1092 - $ref: '#/components/parameters/tagsOneOf'
1090 - $ref: '#/components/parameters/tagsAllOf' 1093 - $ref: '#/components/parameters/tagsAllOf'
1091 - $ref: '#/components/parameters/licenceOneOf' 1094 - $ref: '#/components/parameters/licenceOneOf'
@@ -1226,6 +1229,8 @@ paths:
1226 name: 1229 name:
1227 description: Video name 1230 description: Video name
1228 type: string 1231 type: string
1232 minLength: 3
1233 maxLength: 120
1229 tags: 1234 tags:
1230 description: Video tags (maximum 5 tags each between 2 and 30 characters) 1235 description: Video tags (maximum 5 tags each between 2 and 30 characters)
1231 type: array 1236 type: array
@@ -1397,6 +1402,8 @@ paths:
1397 name: 1402 name:
1398 description: Video name 1403 description: Video name
1399 type: string 1404 type: string
1405 minLength: 3
1406 maxLength: 120
1400 tags: 1407 tags:
1401 description: Video tags (maximum 5 tags each between 2 and 30 characters) 1408 description: Video tags (maximum 5 tags each between 2 and 30 characters)
1402 type: array 1409 type: array
@@ -1523,6 +1530,8 @@ paths:
1523 name: 1530 name:
1524 description: Video name 1531 description: Video name
1525 type: string 1532 type: string
1533 minLength: 3
1534 maxLength: 120
1526 tags: 1535 tags:
1527 description: Video tags (maximum 5 tags each between 2 and 30 characters) 1536 description: Video tags (maximum 5 tags each between 2 and 30 characters)
1528 type: array 1537 type: array
@@ -1627,6 +1636,8 @@ paths:
1627 name: 1636 name:
1628 description: Live video/replay name 1637 description: Live video/replay name
1629 type: string 1638 type: string
1639 minLength: 3
1640 maxLength: 120
1630 tags: 1641 tags:
1631 description: Live video/replay tags (maximum 5 tags each between 2 and 30 characters) 1642 description: Live video/replay tags (maximum 5 tags each between 2 and 30 characters)
1632 type: array 1643 type: array
@@ -1816,10 +1827,10 @@ paths:
1816 reason: 1827 reason:
1817 description: Reason why the user reports this video 1828 description: Reason why the user reports this video
1818 type: string 1829 type: string
1819 minLength: 4 1830 minLength: 2
1831 maxLength: 3000
1820 predefinedReasons: 1832 predefinedReasons:
1821 $ref: '#/components/schemas/PredefinedAbuseReasons' 1833 $ref: '#/components/schemas/PredefinedAbuseReasons'
1822
1823 video: 1834 video:
1824 type: object 1835 type: object
1825 properties: 1836 properties:
@@ -1875,6 +1886,8 @@ paths:
1875 moderationComment: 1886 moderationComment:
1876 type: string 1887 type: string
1877 description: Update the report comment visible only to the moderation team 1888 description: Update the report comment visible only to the moderation team
1889 minLength: 2
1890 maxLength: 3000
1878 responses: 1891 responses:
1879 '204': 1892 '204':
1880 description: successful operation 1893 description: successful operation
@@ -1932,6 +1945,8 @@ paths:
1932 message: 1945 message:
1933 description: Message to send 1946 description: Message to send
1934 type: string 1947 type: string
1948 minLength: 2
1949 maxLength: 3000
1935 required: 1950 required:
1936 - message 1951 - message
1937 responses: 1952 responses:
@@ -2182,6 +2197,7 @@ paths:
2182 parameters: 2197 parameters:
2183 - $ref: '#/components/parameters/channelHandle' 2198 - $ref: '#/components/parameters/channelHandle'
2184 - $ref: '#/components/parameters/categoryOneOf' 2199 - $ref: '#/components/parameters/categoryOneOf'
2200 - $ref: '#/components/parameters/isLive'
2185 - $ref: '#/components/parameters/tagsOneOf' 2201 - $ref: '#/components/parameters/tagsOneOf'
2186 - $ref: '#/components/parameters/tagsAllOf' 2202 - $ref: '#/components/parameters/tagsAllOf'
2187 - $ref: '#/components/parameters/licenceOneOf' 2203 - $ref: '#/components/parameters/licenceOneOf'
@@ -2369,7 +2385,7 @@ paths:
2369 id: 2385 id:
2370 type: integer 2386 type: integer
2371 uuid: 2387 uuid:
2372 type: string 2388 $ref: '#/components/schemas/UUIDv4'
2373 requestBody: 2389 requestBody:
2374 content: 2390 content:
2375 multipart/form-data: 2391 multipart/form-data:
@@ -2379,6 +2395,8 @@ paths:
2379 displayName: 2395 displayName:
2380 description: Video playlist display name 2396 description: Video playlist display name
2381 type: string 2397 type: string
2398 minLength: 1
2399 maxLength: 120
2382 thumbnailfile: 2400 thumbnailfile:
2383 description: Video playlist thumbnail file 2401 description: Video playlist thumbnail file
2384 type: string 2402 type: string
@@ -2432,6 +2450,8 @@ paths:
2432 displayName: 2450 displayName:
2433 description: Video playlist display name 2451 description: Video playlist display name
2434 type: string 2452 type: string
2453 minLength: 1
2454 maxLength: 120
2435 thumbnailfile: 2455 thumbnailfile:
2436 description: Video playlist thumbnail file 2456 description: Video playlist thumbnail file
2437 type: string 2457 type: string
@@ -2825,6 +2845,7 @@ paths:
2825 schema: 2845 schema:
2826 type: string 2846 type: string
2827 - $ref: '#/components/parameters/categoryOneOf' 2847 - $ref: '#/components/parameters/categoryOneOf'
2848 - $ref: '#/components/parameters/isLive'
2828 - $ref: '#/components/parameters/tagsOneOf' 2849 - $ref: '#/components/parameters/tagsOneOf'
2829 - $ref: '#/components/parameters/tagsAllOf' 2850 - $ref: '#/components/parameters/tagsAllOf'
2830 - $ref: '#/components/parameters/licenceOneOf' 2851 - $ref: '#/components/parameters/licenceOneOf'
@@ -3732,9 +3753,7 @@ components:
3732 - type: integer 3753 - type: integer
3733 minimum: 0 3754 minimum: 0
3734 example: 42 3755 example: 42
3735 - type: string 3756 - $ref: '#/components/schemas/UUIDv4'
3736 format: uuid
3737 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
3738 playlistElementId: 3757 playlistElementId:
3739 name: playlistElementId 3758 name: playlistElementId
3740 in: path 3759 in: path
@@ -3793,6 +3812,13 @@ components:
3793 description: The comment id 3812 description: The comment id
3794 schema: 3813 schema:
3795 type: integer 3814 type: integer
3815 isLive:
3816 name: isLive
3817 in: query
3818 required: false
3819 description: whether or not the video is a live
3820 schema:
3821 type: boolean
3796 categoryOneOf: 3822 categoryOneOf:
3797 name: categoryOneOf 3823 name: categoryOneOf
3798 in: query 3824 in: query
@@ -3955,16 +3981,35 @@ components:
3955 moderator: Moderator scope 3981 moderator: Moderator scope
3956 user: User scope 3982 user: User scope
3957 schemas: 3983 schemas:
3958 VideoConstantNumber: 3984 UUIDv4:
3985 type: string
3986 format: uuid
3987 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
3988 pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
3989 # the regex above limits the length;
3990 # however, some tools might require explicit settings:
3991 minLength: 36
3992 maxLength: 36
3993
3994 VideoConstantNumber-Category:
3995 properties:
3996 id:
3997 type: integer
3998 description: category id of the video (see [/videos/categories](#tag/Video/paths/~1videos~1categories/get))
3999 label:
4000 type: string
4001 VideoConstantNumber-Licence:
3959 properties: 4002 properties:
3960 id: 4003 id:
3961 type: integer 4004 type: integer
4005 description: licence id of the video (see [/videos/licences](#tag/Video/paths/~1videos~1licences/get))
3962 label: 4006 label:
3963 type: string 4007 type: string
3964 VideoConstantString: 4008 VideoConstantString-Language:
3965 properties: 4009 properties:
3966 id: 4010 id:
3967 type: string 4011 type: string
4012 description: language id of the video (see [/videos/languages](#tag/Video/paths/~1videos~1languages/get))
3968 label: 4013 label:
3969 type: string 4014 type: string
3970 4015
@@ -4171,14 +4216,21 @@ components:
4171 type: string 4216 type: string
4172 format: url 4217 format: url
4173 VideoStreamingPlaylists: 4218 VideoStreamingPlaylists:
4219 allOf:
4220 - type: object
4221 properties:
4222 id:
4223 type: integer
4224 type:
4225 type: integer
4226 enum:
4227 - 1
4228 description: |
4229 Playlist type:
4230 - `1`: HLS
4231 - $ref: '#/components/schemas/VideoStreamingPlaylists-HLS'
4232 VideoStreamingPlaylists-HLS:
4174 properties: 4233 properties:
4175 id:
4176 type: integer
4177 type:
4178 type: integer
4179 enum:
4180 - 1
4181 description: 'Playlist type (HLS = `1`)'
4182 playlistUrl: 4234 playlistUrl:
4183 type: string 4235 type: string
4184 format: url 4236 format: url
@@ -4187,7 +4239,10 @@ components:
4187 format: url 4239 format: url
4188 files: 4240 files:
4189 type: array 4241 type: array
4190 description: 'Video files associated to this playlist. The difference with the root "files" property is that these files are fragmented, so they can be used in this streaming playlist (HLS etc)' 4242 description: |
4243 Video files associated to this playlist.
4244
4245 The difference with the root `files` property is that these files are fragmented, so they can be used in this streaming playlist (HLS, etc.)
4191 items: 4246 items:
4192 $ref: '#/components/schemas/VideoFile' 4247 $ref: '#/components/schemas/VideoFile'
4193 redundancies: 4248 redundancies:
@@ -4203,52 +4258,69 @@ components:
4203 id: 4258 id:
4204 type: integer 4259 type: integer
4205 uuid: 4260 uuid:
4206 type: string 4261 $ref: '#/components/schemas/UUIDv4'
4207 format: uuid
4208 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4209 name: 4262 name:
4210 type: string 4263 type: string
4264 minLength: 3
4265 maxLength: 120
4211 Video: 4266 Video:
4212 properties: 4267 properties:
4213 id: 4268 id:
4214 type: integer 4269 type: integer
4215 example: 8 4270 example: 8
4216 uuid: 4271 uuid:
4217 type: string 4272 $ref: '#/components/schemas/UUIDv4'
4218 format: uuid
4219 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4220 isLive: 4273 isLive:
4221 type: boolean 4274 type: boolean
4222 createdAt: 4275 createdAt:
4223 type: string 4276 type: string
4224 format: date-time 4277 format: date-time
4278 example: 2017-10-01T10:52:46.396Z
4279 description: time at which the video object was first drafted
4225 publishedAt: 4280 publishedAt:
4226 type: string 4281 type: string
4227 format: date-time 4282 format: date-time
4283 example: 2018-10-01T10:52:46.396Z
4284 description: time at which the video was marked as ready for playback (with restrictions depending on `privacy`). Usually set after a `state` evolution.
4228 updatedAt: 4285 updatedAt:
4229 type: string 4286 type: string
4230 format: date-time 4287 format: date-time
4288 example: 2021-05-04T08:01:01.502Z
4289 description: last time the video's metadata was modified
4231 originallyPublishedAt: 4290 originallyPublishedAt:
4232 type: string 4291 type: string
4233 format: date-time 4292 format: date-time
4293 example: 2010-10-01T10:52:46.396Z
4294 description: used to represent a date of first publication, prior to the practical publication date of `publishedAt`
4234 category: 4295 category:
4235 $ref: '#/components/schemas/VideoConstantNumber' 4296 $ref: '#/components/schemas/VideoConstantNumber-Category'
4236 licence: 4297 licence:
4237 $ref: '#/components/schemas/VideoConstantNumber' 4298 $ref: '#/components/schemas/VideoConstantNumber-Licence'
4238 language: 4299 language:
4239 $ref: '#/components/schemas/VideoConstantString' 4300 $ref: '#/components/schemas/VideoConstantString-Language'
4240 privacy: 4301 privacy:
4241 $ref: '#/components/schemas/VideoPrivacyConstant' 4302 $ref: '#/components/schemas/VideoPrivacyConstant'
4242 description: 4303 description:
4243 type: string 4304 type: string
4305 example: |
4306 **[Want to help to translate this video?](https://weblate.framasoft.org/projects/what-is-peertube-video/)**\r\n\r\n
4307 **Take back the control of your videos! [#JoinPeertube](https://joinpeertube.org)**\r\n*A decentralized video hosting network, based on fr...
4308 minLength: 3
4309 maxLength: 250
4310 description: |
4311 truncated description of the video, written in Markdown.
4312 Resolve `descriptionPath` to get the full description of maximum `10000` characters.
4244 duration: 4313 duration:
4245 type: integer 4314 type: integer
4246 example: 1419 4315 example: 1419
4316 description: duration of the video in seconds
4247 isLocal: 4317 isLocal:
4248 type: boolean 4318 type: boolean
4249 name: 4319 name:
4250 type: string 4320 type: string
4251 example: What is PeerTube? 4321 example: What is PeerTube?
4322 minLength: 3
4323 maxLength: 120
4252 thumbnailPath: 4324 thumbnailPath:
4253 type: string 4325 type: string
4254 example: /static/thumbnails/a65bc12f-9383-462e-81ae-8207e8b434ee.jpg 4326 example: /static/thumbnails/a65bc12f-9383-462e-81ae-8207e8b434ee.jpg
@@ -4301,24 +4373,27 @@ components:
4301 properties: 4373 properties:
4302 descriptionPath: 4374 descriptionPath:
4303 type: string 4375 type: string
4376 example: /api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180a6d/description
4377 description: path at which to get the full description of maximum `10000` characters
4304 support: 4378 support:
4305 type: string 4379 type: string
4306 description: A text tell the audience how to support the video creator 4380 description: A text tell the audience how to support the video creator
4307 example: Please support my work on <insert crowdfunding plateform>! <3 4381 example: Please support my work on <insert crowdfunding plateform>! <3
4382 minLength: 3
4383 maxLength: 1000
4308 channel: 4384 channel:
4309 $ref: '#/components/schemas/VideoChannel' 4385 $ref: '#/components/schemas/VideoChannel'
4310 account: 4386 account:
4311 $ref: '#/components/schemas/Account' 4387 $ref: '#/components/schemas/Account'
4312 tags: 4388 tags:
4313 type: array
4314 items:
4315 type: string
4316 example: [flowers, gardening] 4389 example: [flowers, gardening]
4317 files:
4318 type: array 4390 type: array
4319 description: 'WebTorrent/raw video files. Can be empty if WebTorrent is disabled on the server. In this case, video files will be in the "streamingPlaylists[].files" property' 4391 minItems: 1
4392 maxItems: 5
4320 items: 4393 items:
4321 $ref: '#/components/schemas/VideoFile' 4394 type: string
4395 minLength: 2
4396 maxLength: 30
4322 commentsEnabled: 4397 commentsEnabled:
4323 type: boolean 4398 type: boolean
4324 downloadEnabled: 4399 downloadEnabled:
@@ -4328,10 +4403,24 @@ components:
4328 items: 4403 items:
4329 type: string 4404 type: string
4330 format: url 4405 format: url
4406 files:
4407 type: array
4408 items:
4409 $ref: '#/components/schemas/VideoFile'
4410 description: |
4411 WebTorrent/raw video files. If WebTorrent is disabled on the server:
4412
4413 - field will be empty
4414 - video files will be found in `streamingPlaylists[].files` field
4331 streamingPlaylists: 4415 streamingPlaylists:
4332 type: array 4416 type: array
4333 items: 4417 items:
4334 $ref: '#/components/schemas/VideoStreamingPlaylists' 4418 $ref: '#/components/schemas/VideoStreamingPlaylists'
4419 description: |
4420 HLS playlists/manifest files. If HLS is disabled on the server:
4421
4422 - field will be empty
4423 - video files will be found in `files` field
4335 FileRedundancyInformation: 4424 FileRedundancyInformation:
4336 properties: 4425 properties:
4337 id: 4426 id:
@@ -4367,9 +4456,7 @@ components:
4367 type: string 4456 type: string
4368 format: url 4457 format: url
4369 uuid: 4458 uuid:
4370 type: string 4459 $ref: '#/components/schemas/UUIDv4'
4371 format: uuid
4372 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4373 redundancies: 4460 redundancies:
4374 type: object 4461 type: object
4375 properties: 4462 properties:
@@ -4428,6 +4515,8 @@ components:
4428 reason: 4515 reason:
4429 type: string 4516 type: string
4430 example: The video is a spam 4517 example: The video is a spam
4518 minLength: 2
4519 maxLength: 3000
4431 predefinedReasons: 4520 predefinedReasons:
4432 $ref: '#/components/schemas/AbusePredefinedReasons' 4521 $ref: '#/components/schemas/AbusePredefinedReasons'
4433 reporterAccount: 4522 reporterAccount:
@@ -4437,17 +4526,10 @@ components:
4437 moderationComment: 4526 moderationComment:
4438 type: string 4527 type: string
4439 example: Decided to ban the server since it spams us regularly 4528 example: Decided to ban the server since it spams us regularly
4529 minLength: 2
4530 maxLength: 3000
4440 video: 4531 video:
4441 type: object 4532 $ref: '#/components/schemas/VideoInfo'
4442 properties:
4443 id:
4444 type: integer
4445 name:
4446 type: string
4447 uuid:
4448 type: string
4449 format: uuid
4450 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4451 createdAt: 4533 createdAt:
4452 type: string 4534 type: string
4453 format: date-time 4535 format: date-time
@@ -4457,6 +4539,8 @@ components:
4457 type: integer 4539 type: integer
4458 message: 4540 message:
4459 type: string 4541 type: string
4542 minLength: 2
4543 maxLength: 3000
4460 byModerator: 4544 byModerator:
4461 type: boolean 4545 type: boolean
4462 createdAt: 4546 createdAt:
@@ -4478,12 +4562,14 @@ components:
4478 format: date-time 4562 format: date-time
4479 name: 4563 name:
4480 type: string 4564 type: string
4565 minLength: 3
4566 maxLength: 120
4481 uuid: 4567 uuid:
4482 type: string 4568 $ref: '#/components/schemas/UUIDv4'
4483 format: uuid
4484 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4485 description: 4569 description:
4486 type: string 4570 type: string
4571 minLength: 3
4572 maxLength: 10000
4487 duration: 4573 duration:
4488 type: integer 4574 type: integer
4489 views: 4575 views:
@@ -4498,8 +4584,12 @@ components:
4498 properties: 4584 properties:
4499 displayName: 4585 displayName:
4500 type: string 4586 type: string
4587 minLength: 1
4588 maxLength: 120
4501 description: 4589 description:
4502 type: string 4590 type: string
4591 minLength: 3
4592 maxLength: 1000
4503 isLocal: 4593 isLocal:
4504 type: boolean 4594 type: boolean
4505 ownerAccount: 4595 ownerAccount:
@@ -4508,9 +4598,7 @@ components:
4508 id: 4598 id:
4509 type: integer 4599 type: integer
4510 uuid: 4600 uuid:
4511 type: string 4601 $ref: '#/components/schemas/UUIDv4'
4512 format: uuid
4513 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4514 VideoPlaylist: 4602 VideoPlaylist:
4515 properties: 4603 properties:
4516 id: 4604 id:
@@ -4523,12 +4611,14 @@ components:
4523 format: date-time 4611 format: date-time
4524 description: 4612 description:
4525 type: string 4613 type: string
4614 minLength: 3
4615 maxLength: 1000
4526 uuid: 4616 uuid:
4527 type: string 4617 $ref: '#/components/schemas/UUIDv4'
4528 format: uuid
4529 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
4530 displayName: 4618 displayName:
4531 type: string 4619 type: string
4620 minLength: 1
4621 maxLength: 120
4532 isLocal: 4622 isLocal:
4533 type: boolean 4623 type: boolean
4534 videoLength: 4624 videoLength:
@@ -4552,6 +4642,8 @@ components:
4552 format: url 4642 format: url
4553 text: 4643 text:
4554 type: string 4644 type: string
4645 minLength: 1
4646 maxLength: 10000
4555 threadId: 4647 threadId:
4556 type: integer 4648 type: integer
4557 inReplyToCommentId: 4649 inReplyToCommentId:
@@ -4581,7 +4673,7 @@ components:
4581 VideoCaption: 4673 VideoCaption:
4582 properties: 4674 properties:
4583 language: 4675 language:
4584 $ref: '#/components/schemas/VideoConstantString' 4676 $ref: '#/components/schemas/VideoConstantString-Language'
4585 captionPath: 4677 captionPath:
4586 type: string 4678 type: string
4587 ActorImage: 4679 ActorImage:
@@ -5119,9 +5211,7 @@ components:
5119 type: integer 5211 type: integer
5120 example: 8 5212 example: 8
5121 uuid: 5213 uuid:
5122 type: string 5214 $ref: '#/components/schemas/UUIDv4'
5123 format: uuid
5124 example: 9c9de5e8-0a1e-484a-b099-e80766180a6d
5125 CommentThreadResponse: 5215 CommentThreadResponse:
5126 properties: 5216 properties:
5127 total: 5217 total:
@@ -5370,34 +5460,41 @@ components:
5370 - username 5460 - username
5371 - password 5461 - password
5372 - email 5462 - email
5373 VideoChannelCreate: 5463
5464 VideoChannelCommon:
5374 properties: 5465 properties:
5375 name:
5376 type: string
5377 displayName: 5466 displayName:
5378 type: string 5467 type: string
5468 minLength: 1
5469 maxLength: 120
5379 description: 5470 description:
5380 type: string 5471 type: string
5472 minLength: 3
5473 maxLength: 1000
5381 support: 5474 support:
5382 type: string 5475 type: string
5383 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it' 5476 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it'
5384 example: Please support my work on <insert crowdfunding plateform>! <3 5477 example: Please support my work on <insert crowdfunding plateform>! <3
5478 minLength: 3
5479 maxLength: 1000
5480 VideoChannelCreate:
5481 allOf:
5482 - $ref: '#/components/schemas/VideoChannelCommon'
5483 - properties:
5484 name:
5485 type: string
5486 minLength: 1
5487 maxLength: 120
5385 required: 5488 required:
5386 - name 5489 - name
5387 - displayName 5490 - displayName
5388 VideoChannelUpdate: 5491 VideoChannelUpdate:
5389 properties: 5492 allOf:
5390 displayName: 5493 - $ref: '#/components/schemas/VideoChannelCommon'
5391 type: string 5494 - properties:
5392 description: 5495 bulkVideosSupportUpdate:
5393 type: string 5496 type: boolean
5394 support: 5497 description: 'Update the support field for all videos of this channel'
5395 type: string
5396 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it'
5397 example: Please support my work on <insert crowdfunding plateform>! <3
5398 bulkVideosSupportUpdate:
5399 type: boolean
5400 description: 'Update the support field for all videos of this channel'
5401 5498
5402 MRSSPeerLink: 5499 MRSSPeerLink:
5403 type: object 5500 type: object
diff --git a/support/doc/production.md b/support/doc/production.md
index 8b13041f8..8f3a17f12 100644
--- a/support/doc/production.md
+++ b/support/doc/production.md
@@ -6,7 +6,7 @@
6## Installation 6## Installation
7 7
8Please don't install PeerTube for production on a device behind a low bandwidth connection (example: your ADSL link). 8Please don't install PeerTube for production on a device behind a low bandwidth connection (example: your ADSL link).
9If you want information about the appropriate hardware to run PeerTube, please see the [FAQ](https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md#should-i-have-a-big-server-to-run-peertube). 9If you want information about the appropriate hardware to run PeerTube, please see the [FAQ](https://joinpeertube.org/en_US/faq#should-i-have-a-big-server-to-run-peertube).
10 10
11### Dependencies 11### Dependencies
12 12
diff --git a/yarn.lock b/yarn.lock
index 99a316f71..3ce730fbd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1349,13 +1349,6 @@ async@>=0.2.9, async@^3.0.1, async@^3.1.0:
1349 resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" 1349 resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
1350 integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== 1350 integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
1351 1351
1352async@^2.6.1:
1353 version "2.6.3"
1354 resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
1355 integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
1356 dependencies:
1357 lodash "^4.17.14"
1358
1359async@~0.9.0: 1352async@~0.9.0:
1360 version "0.9.2" 1353 version "0.9.2"
1361 resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" 1354 resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
@@ -3837,7 +3830,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
3837 dependencies: 3830 dependencies:
3838 is-glob "^4.0.1" 3831 is-glob "^4.0.1"
3839 3832
3840glob@7.1.6, glob@^7.0.3, glob@^7.1.3: 3833glob@7.1.6, glob@^7.1.3:
3841 version "7.1.6" 3834 version "7.1.6"
3842 resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 3835 resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
3843 integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 3836 integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -5014,7 +5007,7 @@ lodash@4.17.19:
5014 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" 5007 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
5015 integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== 5008 integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
5016 5009
5017lodash@4.17.21, lodash@>=4.17.13, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: 5010lodash@4.17.21, lodash@>=4.17.13, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21:
5018 version "4.17.21" 5011 version "4.17.21"
5019 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 5012 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
5020 integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 5013 integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -6902,23 +6895,11 @@ resolve-alpn@^1.0.0:
6902 resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.1.tgz#4a006a7d533c81a5dd04681612090fde227cd6e1" 6895 resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.1.tgz#4a006a7d533c81a5dd04681612090fde227cd6e1"
6903 integrity sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ== 6896 integrity sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ==
6904 6897
6905resolve-from@^2.0.0:
6906 version "2.0.0"
6907 resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57"
6908 integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=
6909
6910resolve-from@^4.0.0: 6898resolve-from@^4.0.0:
6911 version "4.0.0" 6899 version "4.0.0"
6912 resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 6900 resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
6913 integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 6901 integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
6914 6902
6915resolve-pkg@^1.0.0:
6916 version "1.0.0"
6917 resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-1.0.0.tgz#e19a15e78aca2e124461dc92b2e3943ef93494d9"
6918 integrity sha1-4ZoV54rKLhJEYdySsuOUPvk0lNk=
6919 dependencies:
6920 resolve-from "^2.0.0"
6921
6922resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0: 6903resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0:
6923 version "1.20.0" 6904 version "1.20.0"
6924 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" 6905 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
@@ -7054,16 +7035,6 @@ sax@>=0.6.0, sax@^1.2.4:
7054 resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" 7035 resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
7055 integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== 7036 integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
7056 7037
7057scripty@^2.0.0:
7058 version "2.0.0"
7059 resolved "https://registry.yarnpkg.com/scripty/-/scripty-2.0.0.tgz#25761bb2e237a7563f705d87357db07791d38459"
7060 integrity sha512-vbd4FPeuNwYNGtRtYa1wDZLPCx5PpW6VrldCEiBGqPz7Je1xZOgNvVPD2axymvqNghBIRiXxAU+JwYrOzvuLJg==
7061 dependencies:
7062 async "^2.6.1"
7063 glob "^7.0.3"
7064 lodash "^4.17.11"
7065 resolve-pkg "^1.0.0"
7066
7067semver-diff@^3.1.1: 7038semver-diff@^3.1.1:
7068 version "3.1.1" 7039 version "3.1.1"
7069 resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" 7040 resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"