diff --git a/frontend/src/static/js/components/media-page/MediaPage.scss b/frontend/src/static/js/components/media-page/MediaPage.scss index 60160a89..b2ca58fc 100755 --- a/frontend/src/static/js/components/media-page/MediaPage.scss +++ b/frontend/src/static/js/components/media-page/MediaPage.scss @@ -1,2039 +1,2059 @@ @use "sass:math"; -@import '../../../css/includes/_variables.scss'; -@import '../../../css/includes/_variables_dimensions.scss'; +@import "../../../css/includes/_variables.scss"; +@import "../../../css/includes/_variables_dimensions.scss"; .visible-sidebar .page-main-wrap { - padding-left: 0; + padding-left: 0; } .media-author-actions { - .popup-message-bottom { - button { - &.cancel-comment-removal { - color: var(--media-author-actions-popup-bottom-cancel-removal-button-text-color); + .popup-message-bottom { + button { + &.cancel-comment-removal { + color: var(--media-author-actions-popup-bottom-cancel-removal-button-text-color); - &:hover, - &:focus { - color: var(--media-author-actions-popup-bottom-cancel-removal-button-hover-text-color); + &:hover, + &:focus { + color: var(--media-author-actions-popup-bottom-cancel-removal-button-hover-text-color); - .material-icons { - color: var(--media-author-actions-popup-bottom-cancel-removal-button-hover-icon-text-color); - } + .material-icons { + color: var(--media-author-actions-popup-bottom-cancel-removal-button-hover-icon-text-color); + } + } + } } - } } - } } .profile-banner-wrap { - .popup-message-bottom { - button { - &.cancel-profile-removal { - color: var(--profile-banner-wrap-popup-bottom-cancel-removal-button-text-color); + .popup-message-bottom { + button { + &.cancel-profile-removal { + color: var(--profile-banner-wrap-popup-bottom-cancel-removal-button-text-color); - &:hover, - &:focus { - color: var(--profile-banner-wrap-popup-bottom-cancel-removal-button-hover-text-color); + &:hover, + &:focus { + color: var(--profile-banner-wrap-popup-bottom-cancel-removal-button-hover-text-color); - .material-icons { - color: var(--profile-banner-wrap-popup-bottom-cancel-removal-button-hover-icon-text-color); - } + .material-icons { + color: var(--profile-banner-wrap-popup-bottom-cancel-removal-button-hover-icon-text-color); + } + } + } } - } } - } } .media-title-banner { - border-color: var(--media-title-banner-border-color); + border-color: var(--media-title-banner-border-color); - .media-labels-area { - .media-label-state { - > * { - color: var(--media-title-labels-area-text-color); - background-color: var(--media-title-labels-area-bg-color); - } + .media-labels-area { + .media-label-state { + > * { + color: var(--media-title-labels-area-text-color); + background-color: var(--media-title-labels-area-bg-color); + } + } + + .helper-icon { + .material-icons { + color: var(--media-title-labels-area-text-color); + } + } } - .helper-icon { - .material-icons { - color: var(--media-title-labels-area-text-color); - } + .media-views { + color: var(--media-title-views-text-color); } - } - .media-views { - color: var(--media-title-views-text-color); - } + .media-actions { + > * { + > * { + > *:not(.popup) { + .circle-icon-button { + &:focus { + > * { + background-color: var(--media-actions-not-popup-circle-icon-focus-bg-color); + } + } - .media-actions { - > * { - > * { - > *:not(.popup) { - .circle-icon-button { - &:focus { - > * { - background-color: var(--media-actions-not-popup-circle-icon-focus-bg-color); - } - } - - &:active { - > * { - background-color: var(--media-actions-not-popup-circle-icon-active-bg-color); - } - } - } - } - - &.like, - &.dislike { - &:before { - border-color: var(--media-actions-like-before-border-color); - } - } - - &.share { - .share-popup-title { - color: var(--media-actions-share-title-text-color); - } - - .share-options { - .previous-slide, - .next-slide { - .circle-icon-button { - color: var(--media-actions-share-options-nav-button-text-color); - } - } - - .sh-option { - a, - button { - color: var(--media-actions-share-options-link-text-color); - } - } - } - - .copy-field { - > div { - border-color: var(--media-actions-share-copy-field-border-color); - background-color: var(--media-actions-share-copy-field-bg-color); - } - - input[type='text'] { - color: var(--media-actions-share-copy-field-input-text-color); - } - } - } - - &.more-options, - &.video-downloads { - .nav-page-main, - .nav-page-mediaStatusInfo, - .nav-page-videoDownloadOptions { - .popup { - > * { - background-color: var(--media-actions-more-options-popup-bg-color); - } - } - } - - .nav-page-main, - .nav-page-videoDownloadOptions { - .popup { - > * { - &.main-options, - &.video-download-options { - .nav-menu a, - .nav-menu button { - color: var(--media-actions-more-options-popup-nav-link-text-color); - } + &:active { + > * { + background-color: var(--media-actions-not-popup-circle-icon-active-bg-color); + } + } + } } - } - } - } - } - &.share, - &.save, - &.more-options, - &.video-downloads { - .popup-fullscreen { - .popup-main { - > div { - background-color: var(--media-actions-share-fullscreen-popup-main-bg-color); - } + &.like, + &.dislike { + &:before { + border-color: var(--media-actions-like-before-border-color); + } + } + + &.share { + .share-popup-title { + color: var(--media-actions-share-title-text-color); + } + + .share-options { + .previous-slide, + .next-slide { + .circle-icon-button { + color: var(--media-actions-share-options-nav-button-text-color); + } + } + + .sh-option { + a, + button { + color: var(--media-actions-share-options-link-text-color); + } + } + } + + .copy-field { + > div { + border-color: var(--media-actions-share-copy-field-border-color); + background-color: var(--media-actions-share-copy-field-bg-color); + } + + input[type="text"] { + color: var(--media-actions-share-copy-field-input-text-color); + } + } + } + + &.more-options, + &.video-downloads { + .nav-page-main, + .nav-page-mediaStatusInfo, + .nav-page-videoDownloadOptions { + .popup { + > * { + background-color: var(--media-actions-more-options-popup-bg-color); + } + } + } + + .nav-page-main, + .nav-page-videoDownloadOptions { + .popup { + > * { + &.main-options, + &.video-download-options { + .nav-menu a, + .nav-menu button { + color: var(--media-actions-more-options-popup-nav-link-text-color); + } + } + } + } + } + } + + &.share, + &.save, + &.more-options, + &.video-downloads { + .popup-fullscreen { + .popup-main { + > div { + background-color: var(--media-actions-share-fullscreen-popup-main-bg-color); + } + } + } + } } - } } - } } - } } .report-form { - .form-title { - color: var(--report-form-title-text-color); - } - - .form-field { - .label { - color: var(--report-form-field-label-text-color); + .form-title { + color: var(--report-form-title-text-color); } - input[type='text'], - textarea { - color: var(--report-form-field-input-text-color); - border-color: var(--report-form-field-input-border-color); - background-color: var(--report-form-field-input-bg-color); - } - } + .form-field { + .label { + color: var(--report-form-field-label-text-color); + } - .form-help-text { - color: var(--report-form-help-text-color); - } + input[type="text"], + textarea { + color: var(--report-form-field-input-text-color); + border-color: var(--report-form-field-input-border-color); + background-color: var(--report-form-field-input-bg-color); + } + } + + .form-help-text { + color: var(--report-form-help-text-color); + } } .form-actions-bottom { - border-top-color: var(--form-actions-bottom-border-top-color); + border-top-color: var(--form-actions-bottom-border-top-color); } .media-info-content { - .media-author-banner { - .author-banner-name { - color: var(--media-author-banner-name-text-color); + .media-author-banner { + .author-banner-name { + color: var(--media-author-banner-name-text-color); + } + + .author-banner-date { + color: var(--media-author-banner-date-text-color); + } } - .author-banner-date { - color: var(--media-author-banner-date-text-color); + .media-content-banner { + border-color: var(--media-content-banner-border-color); } - } - - .media-content-banner { - border-color: var(--media-content-banner-border-color); - } } .share-embed-inner { - .on-right-top, - .on-right-bottom { - border-color: var(--share-embed-inner-on-right-border-color); - } - - .on-right-top { - .on-right-top-inner { - .ttl { - color: var(--share-embed-inner-on-right-ttl-text-color); - } - - .circle-icon-button { - color: var(--share-embed-inner-on-right-icon-text-color); - } + .on-right-top, + .on-right-bottom { + border-color: var(--share-embed-inner-on-right-border-color); } - } - .on-right-middle { - textarea { - color: var(--share-embed-inner-textarea-text-color); - border-color: var(--share-embed-inner-textarea-border-color); - background-color: var(--share-embed-inner-textarea-bg-color); - } - } + .on-right-top { + .on-right-top-inner { + .ttl { + color: var(--share-embed-inner-on-right-ttl-text-color); + } - .media-embed-wrap { - .circle-icon-button { - color: var(--share-embed-inner-embed-wrap-iconn-text-color); + .circle-icon-button { + color: var(--share-embed-inner-on-right-icon-text-color); + } + } + } + + .on-right-middle { + textarea { + color: var(--share-embed-inner-textarea-text-color); + border-color: var(--share-embed-inner-textarea-border-color); + background-color: var(--share-embed-inner-textarea-bg-color); + } + } + + .media-embed-wrap { + .circle-icon-button { + color: var(--share-embed-inner-embed-wrap-iconn-text-color); + } } - } } .media-status-info { - li { - span { - color: var(--media-status-info-item-text-color); + li { + span { + color: var(--media-status-info-item-text-color); + } } - } } .viewer-sidebar { - .auto-play { - .item { - border-bottom-color: var(--viewer-sidebar-auto-play-border-bottom-color); - } - } - - .auto-play-header { - .next-label { - color: var(--viewer-sidebar-auto-play-next-label-text-color); + .auto-play { + .item { + border-bottom-color: var(--viewer-sidebar-auto-play-border-bottom-color); + } } - .auto-play-option { - color: var(--viewer-sidebar-auto-play-option-text-color); + .auto-play-header { + .next-label { + color: var(--viewer-sidebar-auto-play-next-label-text-color); + } + + .auto-play-option { + color: var(--viewer-sidebar-auto-play-option-text-color); + } } - } } .viewer-section { - position: relative; - margin: 0 auto; - width: 100%; + position: relative; + margin: 0 auto; + width: 100%; - max-width: 1280px + (2 * 24); - .viewer-section.viewer-section-nested { max-width: 1280px + (2 * 24); - } - - &.viewer-wide { - max-width: 1754px; .viewer-section.viewer-section-nested { - max-width: 1754px; + max-width: 1280px + (2 * 24); } - } - &.theater-mode { - max-width: 100%; - - .video-js.vjs-mediacms { - padding: 0; + &.viewer-wide { + max-width: 1754px; + .viewer-section.viewer-section-nested { + max-width: 1754px; + } + } + + &.theater-mode { + max-width: 100%; + + .video-js.vjs-mediacms { + padding: 0; + } } - } } .viewer-container, .viewer-info { - position: relative; - float: left; - width: 100%; + position: relative; + float: left; + width: 100%; } .viewer-container { - .vjs-theater-mode-control { - display: none !important; - } - - @media screen and (min-width: 640px) { - padding: 24px 24px 0 24px; - .vjs-theater-mode-control { - display: inline-block !important; + display: none !important; } - } - .viewer-section.theater-mode & { - width: 100%; - padding: 0; - } + @media screen and (min-width: 640px) { + padding: 24px 24px 0 24px; - .vjs-fullscreen .vjs-theater-mode-control { - display: none !important; - } + .vjs-theater-mode-control { + display: inline-block !important; + } + } + + .viewer-section.theater-mode & { + width: 100%; + padding: 0; + } + + .vjs-fullscreen .vjs-theater-mode-control { + display: none !important; + } } .viewer-info { } .viewer-info-inner { - @media screen and (min-width: 640px) { - margin: 0 24px; - } + @media screen and (min-width: 640px) { + margin: 0 24px; + } } .viewer-sidebar { - position: relative; - float: right; - width: 100%; - - padding: 12px 16px 24px; - - @media screen and (min-width: 640px) { - padding: 24px; - } - - .load-more { - margin-top: 16px; - margin-bottom: 0; - - &:focus { - box-shadow: none; - } - } - - .auto-play { - .items-list-wrap { - min-height: 0; - } - - .item { - margin-bottom: 8px * 2; - padding-bottom: 8px * 2; - - border-bottom-width: 1px; - border-bottom-style: solid; - } - } - - .auto-play-header { position: relative; + float: right; width: 100%; - min-height: 22px; - display: table; - margin: 0 auto 24px; - @media screen and (min-width: 1008px) { - margin: 0 auto 12px; + padding: 12px 16px 24px; + + @media screen and (min-width: 640px) { + padding: 24px; } - > * { - display: table-cell; - vertical-align: middle; - } + .load-more { + margin-top: 16px; + margin-bottom: 0; - .next-label { - font-size: 16px; - line-height: 20px; - } - - .auto-play-option { - text-align: right; - - label { - font-weight: 500; - font-size: 0.928571429em; - letter-spacing: 0.007px; - margin: 0; - } - - .checkbox-label { &:focus { - outline: 0; + box-shadow: none; + } + } + + .auto-play { + .items-list-wrap { + min-height: 0; + } + + .item { + margin-bottom: 8px * 2; + padding-bottom: 8px * 2; + + border-bottom-width: 1px; + border-bottom-style: solid; + } + } + + .auto-play-header { + position: relative; + width: 100%; + min-height: 22px; + display: table; + margin: 0 auto 24px; + + @media screen and (min-width: 1008px) { + margin: 0 auto 12px; + } + + > * { + display: table-cell; + vertical-align: middle; + } + + .next-label { + font-size: 16px; + line-height: 20px; + } + + .auto-play-option { + text-align: right; + + label { + font-weight: 500; + font-size: 0.928571429em; + letter-spacing: 0.007px; + margin: 0; + } + + .checkbox-label { + &:focus { + outline: 0; + } + } + + .selectbox { + width: 1.231em; + height: 1.231em; + margin-top: -2px; + } } - } - - .selectbox { - width: 1.231em; - height: 1.231em; - margin-top: -2px; - } } - } } @media screen and (min-width: 1008px) { - .viewer-info { - width: (100 - 41.602) * 1%; - } + .viewer-info { + width: (100 - 41.602) * 1%; + } - .viewer-sidebar { - width: 41.602%; - padding: 24px 24px 24px 0; - } + .viewer-sidebar { + width: 41.602%; + padding: 24px 24px 24px 0; + } } @media screen and (min-width: 1216px) { - .viewer-container, - .viewer-info { - width: (100 - 32.079) * 1%; + .viewer-container, + .viewer-info { + width: (100 - 32.079) * 1%; - .viewer-wide & { - width: (100 - 31.187) * 1%; + .viewer-wide & { + width: (100 - 31.187) * 1%; + } } - } - .viewer-sidebar { - width: 32.079%; + .viewer-sidebar { + width: 32.079%; - .viewer-wide & { - width: 31.187%; + .viewer-wide & { + width: 31.187%; + } } - } } @media screen and (min-width: 1440px) { - .viewer-container, - .viewer-info { - width: (100 - 32.079) * 1%; + .viewer-container, + .viewer-info { + width: (100 - 32.079) * 1%; - .viewer-wide & { - width: (100 - 24.288) * 1%; + .viewer-wide & { + width: (100 - 24.288) * 1%; + } } - } - .viewer-sidebar { - width: 32.079%; + .viewer-sidebar { + width: 32.079%; - .viewer-wide & { - width: 24.288%; + .viewer-wide & { + width: 24.288%; + } } - } } .viewer-container { - .player-container { + .player-container { + position: relative; + padding-top: math.div(9, 16) * 100%; + + @media screen and (min-width: 640px) { + .viewer-section.theater-mode & { + padding-top: 40.1%; + } + } + + &.audio-player-container { + &:before { + content: "\E3A1"; + position: absolute; + top: 50%; + left: 50%; + margin: 0; + margin-top: -2rem; + margin-left: -2rem; + font-size: 4rem; + + line-height: 1; + padding: 0; + font-family: "Material Icons"; + text-decoration: none; + color: #888; + } + + .vjs-big-play-button { + } + + .vjs-control-bar { + transform: none !important; + } + } + } + + .player-container-inner { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +} + +.embedded-app { + .viewer-container, + .viewer-info { + width: 100%; + } +} + +.viewer-image-container { position: relative; - padding-top: math.div(9, 16) * 100%; + display: block; + + img { + cursor: pointer; + position: relative; + display: block; + max-width: 100%; + height: auto; + margin: 0 auto; + } +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.slideshow-container { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: auto; + max-width: 90%; +} + +.slideshow-image img { + display: block; + width: auto; + height: auto; + max-width: 100%; /* Ensure image doesn't exceed container width */ + max-height: 90vh; + border-radius: 0; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); + transition: + transform 60s ease-in-out, + opacity 60 ease-in-out; +} + +.slideshow-title { + margin-top: 10px; + text-align: start; + font-size: 16px; + font-weight: bold; + color: #bdb6b6; + z-index: 1200; +} + +.arrow { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + width: 40px; + height: 40px; + top: 50%; + transform: translateY(-50%); + border: none; + color: white; + font-size: 2rem; + background-color: rgba(0, 0, 0, 0.2); + cursor: pointer; + padding: 10px; + border-radius: 50%; + z-index: 1000; + transition: + background-color 0.2s ease, + transform 0.2s ease; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8); +} + +.arrow:hover { + background: rgba(92, 78, 78, 0.6); + transform: translateY(-50%) scale(1.1); +} + +.arrow.left { + left: 10px; +} + +.arrow.right { + right: 10px; +} + +.thumbnail-navigation { + position: fixed; + display: flex; + align-items: center; + justify-content: center; + margin-top: 20px; + gap: 10px; + bottom: 10%; + left: 50%; + transform: translateX(-50%); +} + +.thumbnail-container { + display: flex; + gap: 10px; + overflow-x: auto; + scroll-behavior: smooth; + max-width: 80%; + padding: 10px 0; + scrollbar-width: none; /* Hide scrollbar for Firefox */ +} + +.thumbnail-container.center-thumbnails { + display: flex; + justify-content: center; + overflow: visible; /* No scrollbars for fewer thumbnails */ +} + +.thumbnail-container::-webkit-scrollbar { + display: none; /* Hide scrollbar for Chrome/Safari */ +} + +.thumbnail { + width: 60px; + height: 60px; + object-fit: cover; + border: 2px solid transparent; + border-radius: 5px; + cursor: pointer; + transition: transform 0.3s ease; +} + +.thumbnail.active { + border-color: white; +} + +.thumbnail:hover { + transform: scale(1.1); +} + +.viewer-container .player-container { + @media screen and (min-width: 480px) { + border-radius: 10px; + } +} + +.viewer-container .player-container.audio-player-container { + @media screen and (min-width: 480px) { + padding-top: 0.75 * 56.25%; + } @media screen and (min-width: 640px) { - .viewer-section.theater-mode & { - padding-top: 40.1%; - } + padding-top: 0.5 * 56.25%; } - &.audio-player-container { - &:before { - content: '\E3A1'; - position: absolute; - top: 50%; - left: 50%; - margin: 0; - margin-top: -2rem; - margin-left: -2rem; - font-size: 4rem; + .video-js.vjs-mediacms { + padding-top: 0; + } +} +.viewer-container .pdf-container { + overflow-y: auto; + display: flex; + justify-content: center; + align-items: center; + width: 100%; // Default width for mobile + height: 400px; // Default height for mobile + + @media (min-width: 768px) and (max-width: 1023px) { + // Tablets + width: 90%; + height: 600px; + } + + @media (min-width: 1024px) { + // Desktop + width: 85%; + height: 900px; + } +} + +.viewer-container .player-container.viewer-pdf-container, +.viewer-container .player-container.viewer-attachment-container { + background-color: var(--item-thumb-bg-color); + min-height: 0.5 * 259px; + padding-top: 0.25 * 56.25%; + + .player-container-inner { + .material-icons { + font-size: 4rem; + color: #888; + } + + > span { + position: absolute; + display: table; + top: 0; + left: 0; + width: 100%; + height: 100%; + + span { + display: table-cell; + text-align: center; + vertical-align: middle; + } + } + } +} + +.media-author-actions { + position: relative; + display: flex; + align-items: center; + gap: 12px; + font-family: inherit; + margin-bottom: -8px; + + button { + &:focus { + box-shadow: none; + } + } + + .edit-media-icon, + .remove-media-icon { + text-decoration: none; + color: #fff; + border: 0; line-height: 1; padding: 0; - font-family: 'Material Icons'; - text-decoration: none; - color: #888; - } + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + .material-icons { + font-size: 20px; + line-height: 1; + } - .vjs-big-play-button { - } - - .vjs-control-bar { - transform: none !important; - } + &:active { + transform: scale(0.98); + } } - } - .player-container-inner { + .edit-media-icon { + background-color: rgba(0, 153, 51, 0.9); + + &:hover { + background-color: rgba(0, 153, 51, 1); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transform: scale(1.05); + } + + .dark_theme & { + background-color: rgba(102, 187, 102, 0.9); + + &:hover { + background-color: rgba(102, 187, 102, 1); + } + } + } + + .remove-media-icon { + background-color: rgba(220, 53, 69, 0.9); + + &:hover { + background-color: rgba(220, 53, 69, 1); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transform: scale(1.05); + } + + .dark_theme & { + background-color: rgba(255, 107, 107, 0.9); + + &:hover { + background-color: rgba(255, 107, 107, 1); + } + } + } + + .popup { + position: absolute; + bottom: 100%; + left: 0; + margin-bottom: 8px; + } + + .popup-message-bottom { + button { + position: relative; + width: auto; + padding: 0; + border: 0; + background: none; + + &.proceed-comment-removal { + float: right; + } + + &.cancel-comment-removal { + float: left; + } + } + } +} + +.media-title-banner { + position: relative; + border-bottom-width: 1px; + border-bottom-style: solid; + + min-height: 64px; + + padding: 20px 12px 0 16px; + + @media screen and (min-width: 640px) { + padding: 20px 0 0; + } + + h1 { + display: inline-block; + font-size: 18px; + font-weight: 400; + line-height: 1.333333; + margin: 0; + + white-space: pre-wrap; + overflow-wrap: break-word; + } + + .media-labels-area { + position: relative; + display: block; + + .media-labels-area-inner { + display: table; + + > * { + display: table-cell; + vertical-align: middle; + } + } + + .media-label-state { + > * { + padding: 2px 4px; + margin-right: 4px; + font-size: 12px; + line-height: 1; + font-weight: 500; + text-transform: capitalize; + border-radius: 2px; + } + } + + .helper-icon { + padding: 0 4px 1px 0; + cursor: help; + + .material-icons { + display: inline-block; + padding: 0; + margin: 0; + font-size: 16px; + line-height: 1; + } + } + } + + .media-views-actions { + position: relative; + display: table; + width: 100%; + min-height: 40px; + z-index: 999; + + > * { + display: table-cell; + vertical-align: middle; + } + } + + .media-views { + line-height: 1.25; + font-family: inherit; + } + + .media-actions { + > * { + position: relative; + float: right; + + > * { + position: relative; + width: auto; + float: left; + margin-left: 8px; + padding-bottom: 8px; + color: rgb(144, 144, 144); + + button, + .circle-icon-button { + color: rgb(144, 144, 144); + + &:focus { + box-shadow: none; + } + } + + > *:not(.popup) { + display: table; + cursor: pointer; + + > * { + display: table-cell; + vertical-align: middle; + + &:nth-child(2) { + padding-right: 8px; + } + + @media screen and (max-width: 480px) { + &:nth-child(2) { + display: none !important; + } + } + } + + .circle-icon-button { + display: inline-block; + background-color: transparent; + + @media screen and (max-width: 359px) { + width: 2.25rem; + height: 2.25rem; + } + + @media screen and (max-width: 319px) { + width: 2rem; + height: 2rem; + } + + @media screen and (max-width: 299px) { + width: 1.75rem; + height: 1.75rem; + } + } + } + + .popup { + position: absolute; + top: 100%; + right: 0; + margin-top: -4px; + } + + &.like, + &.dislike, + &.share, + &.save, + &.download, + &.video-downloads { + > *:not(.popup) { + font-size: 13px; + font-weight: 500; + border: 0; + background: none; + + > * { + display: inline-block; + } + } + } + + &.like, + &.dislike, + &.more-options { + letter-spacing: 0.0007px; + } + + &.like, + &.dislike { + &:before { + content: ""; + position: absolute; + bottom: 0; + left: -4px; + right: -4px; + border-bottom: 2px solid; + } + } + + &.like { + } + + &.dislike { + } + + &.share { + .scrollable-content { + display: block; + padding-bottom: 8px; + overflow: auto; + } + + .share-popup-title { + margin-bottom: 16px; + line-height: 1.25; + } + + .share-options { + margin-bottom: 16px; + + .share-options-inner { + position: relative; + display: block; + white-space: nowrap; + overflow: hidden; + -webkit-overflow-scrolling: touch; + scroll-behavior: smooth; + } + + .previous-slide, + .next-slide { + position: absolute; + top: 78px; + z-index: +1; + + .circle-icon-button { + } + } + + .previous-slide { + left: 8px; + } + + .next-slide { + right: 8px; + } + + .sh-option { + vertical-align: top; + position: relative; + display: inline-block; + padding-right: 8px; + text-align: center; + + a, + button { + padding: 5px 5px 2px; + margin: 1px 0; + display: block; + text-decoration: none; + + outline: 0; + border: 0; + background: none; + + > *:first-child { + display: block; + width: 60px; + height: 60px; + line-height: 60px; + margin: 0 auto 8px; + border-radius: 50%; + background-position: center; + background-repeat: no-repeat; + } + + > *:last-child { + font-size: 13px; + line-height: 18px; + overflow: hidden; + } + + .material-icons { + padding: 0; + margin: 0 0 0 1px; + line-height: 1; + font-size: 30px; + overflow: hidden; + color: #fff; + } + } + } + + .share-embed-opt { + a, + button { + > *:first-child { + background-color: rgb(244, 244, 244); + } + + .material-icons { + color: rgb(111, 111, 111); + } + } + } + + .share-email { + a, + button { + > *:first-child { + background-color: rgb(136, 136, 136); + } + } + } + } + + .copy-field { + $button-width: 5.5rem; + + position: relative; + width: 100%; + + > div { + display: block; + padding-right: $button-width; + border-width: 1px; + border-style: solid; + border-radius: 2px; + } + + input[type="text"] { + width: 100%; + height: 42px; + padding: 1px 0 1px 16px; + font-family: Arial; + font-size: 14px; + line-height: normal; + border: 0; + background: none; + } + + button { + position: absolute; + top: 0; + right: 0; + width: $button-width; + height: 100%; + line-height: 20px; + border: 0; + background: none; + font-size: 14px; + font-weight: 500; + } + } + } + + &.share, + &.save, + &.download a { + color: rgb(144, 144, 144); + } + + &.more-options, + &.video-downloads { + .circle-icon-button { + outline-width: initial; + } + + .nav-page-main, + .nav-page-mediaStatusInfo, + .nav-page-videoDownloadOptions { + .popup { + width: auto; + overflow: visible; + box-shadow: none; + background-color: transparent; + + > * { + @media screen and (max-width: 1007px) { + width: 300px; + } + + @media screen and (min-width: 1008px) { + width: 368px; + } + + @media screen and (max-width: 480px) { + width: 220px; + } + + box-shadow: + 0 16px 24px 2px rgba(#000, 0.14), + 0 6px 30px 5px rgba(#000, 0.12), + 0 8px 10px -5px rgba(#000, 0.4); + + &.main-options, + &.video-download-options { + width: 240px; + box-shadow: + 0 2px 2px 0 rgba(#000, 0.14), + 0 1px 5px 0 rgba(#000, 0.12), + 0 3px 1px -2px rgba(#000, 0.2); + } + } + } + } + + .nav-page-main, + .nav-page-videoDownloadOptions { + .popup { + > * { + &.main-options, + &.video-download-options { + width: 160px; + + .nav-menu { + padding: 8px 0; + + .menu-item-icon { + margin-right: 16px; + } + } + + .nav-menu a, + .nav-menu button { + padding: 0 16px; + font-size: 13px; + font-weight: 400; + + > * { + line-height: 48px; + } + } + } + } + } + } + } + + &.share, + &.save, + &.more-options, + &.video-downloads { + .popup-fullscreen { + .popup-main { + overflow: visible; + + > div { + position: relative; + + width: auto; + width: 100%; + max-width: 518px; + + display: inline-block; + margin: 0 auto; + padding: 24px; + text-align: initial; + + box-shadow: + rgba(#000, 0.14) 0px 16px 24px 2px, + rgba(#000, 0.12) 0px 6px 30px 5px, + rgba(#000, 0.4) 0px 8px 10px; + } + } + } + } + + &.video-downloads { + .circle-icon-button { + outline-width: initial; + } + + .nav-page-main, + .nav-page-videoDownloadOptions { + .popup { + width: auto; + overflow: visible; + box-shadow: none; + background-color: transparent; + + > * { + @media screen and (max-width: 1007px) { + width: 300px; + } + + @media screen and (min-width: 1008px) { + width: 368px; + } + + @media screen and (max-width: 480px) { + width: 220px; + } + + box-shadow: + 0 16px 24px 2px rgba(#000, 0.14), + 0 6px 30px 5px rgba(#000, 0.12), + 0 8px 10px -5px rgba(#000, 0.4); + + &.main-options, + &.video-download-options { + width: 240px; + box-shadow: + 0 2px 2px 0 rgba(#000, 0.14), + 0 1px 5px 0 rgba(#000, 0.12), + 0 3px 1px -2px rgba(#000, 0.2); + + .popup-main { + min-height: 0; + max-height: 16em; + max-height: 33.333vh; + + overflow-x: hidden; + overflow-y: auto; + } + } + } + } + } + } + } + } + + .disliked-media { + > * { + > * { + &.dislike { + &:before { + } + } + } + } + } + } + + .media-views-actions { + &.liked-media { + .media-actions { + > * { + > * { + &.like, + &.like button, + &.like .circle-icon-button { + } + + &.like, + &.dislike { + &:before { + } + } + } + } + } + } + + &.disliked-media { + .media-actions { + > * { + > * { + &.dislike, + &.dislike button, + &.dislike .circle-icon-button { + } + + &.like, + &.dislike { + &:before { + } + } + } + } + } + } + } +} + +.nav-page-loggedInReportMedia .popup-main > div { + padding: 0 !important; + max-width: 450px !important; +} + +.report-form { + display: block; + overflow: auto; + max-height: 50%; + + .form-title { + padding: 20px 24px 0; + } + + .form-field { + padding: 24px 24px; + + + .form-field { + padding: 0 24px 24px 24px; + } + + position: relative; + display: block; + + .label { + display: block; + line-height: 1.5; + font-size: 13px; + margin-bottom: 8px; + font-weight: 500; + } + + input[type="text"], + textarea { + min-width: 100%; + width: 100%; + max-width: 100%; + height: 42px; + padding: 1px 12px; + margin: 0; + font-family: Arial; + font-size: 14px; + line-height: normal; + + border-width: 1px; + border-style: solid; + + border-radius: 2px; + } + + *[readonly] { + cursor: default; + cursor: not-allowed; + } + + input[type="text"] { + font-size: 14px; + } + + textarea { + line-height: 1.4; + padding: 8px 12px; + min-height: 80px; + min-height: 20vh; + max-height: 50vh; + } + } + + .form-help-text { + font-family: inherit; + font-size: 12px; + line-height: 15px; + margin-bottom: 8px; + } +} + +.form-actions-bottom { + display: block; + text-align: right; + padding: 8px; + border-top-width: 1px; + border-top-style: solid; + + button { + font-size: 14px; + font-stretch: 100%; + font-weight: 500; + line-height: 20px; + letter-spacing: 0.007px; + text-align: center; + padding: 10px 16px; + margin: 0; + border: 0; + background: none; + + &.cancel { + color: inherit !important; + } + } +} + +.media-info-content { + .media-author-banner { + display: table; + padding: 0 16px; + margin: 16px 0; + + @media screen and (min-width: 640px) { + padding: 0; + margin: 16px 0 10px; + } + + > * { + display: table-cell; + vertical-align: middle; + } + + .author-banner-thumb { + span { + display: block; + width: 48px; + height: 48px; + margin-right: 16px; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + overflow: hidden; + border-radius: 50%; + + img { + width: 100%; + height: 100%; + opacity: 0; + } + } + } + + .author-banner-name { + font-size: 14px; + font-weight: 500; + line-height: 1; + text-decoration: none; + } + + .author-banner-date { + display: block; + font-size: 14px; + line-height: 1.384615; + font-family: inherit; + } + } + + .media-content-banner { + white-space: pre-wrap; + overflow-wrap: break-word; + + font-size: 14px; + line-height: 1.5; + + padding-bottom: 16px; + margin-bottom: 8px; + border-bottom-width: 1px; + border-bottom-style: solid; + + padding-left: 16px; + padding-right: 16px; + + margin-top: 20px; + + @media screen and (min-width: 640px) { + padding-right: 0; + padding-left: 0; + } + + @media screen and (min-width: 1008px) { + margin-bottom: 0; + border: 0; + } + + .media-content-banner-inner { + font-family: inherit; + + > * { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } + } + + .load-more { + margin-bottom: 24px; + } + } + + .media-content-description { + p, + ul { + margin: 0; + } + } + } + + .media-author-banner + .media-content-banner { + margin-top: 0; + padding-top: 16px; + + border-top-width: 1px; + border-top-style: solid; + + @media screen and (min-width: 640px) { + padding-top: 0; + padding-left: 64px; + border-top-width: 0; + } + + @media screen and (min-width: 1008px) { + border: 0; + } + + .media-content-banner-inner { + max-width: 615px; + } + } +} + +.media-content-field { + position: relative; + width: 100%; + display: inline-block; + font-family: inherit; + line-height: 21px; + color: rgb(136, 136, 136); +} + +.media-content-field-label { + position: relative; + display: inline-block; + width: 126px; + padding-right: 16px; + + position: absolute; + top: 0; + left: 0; + + h4 { + margin: 0; + font-size: 14px; + font-weight: 400; + line-height: 21px; + } +} + +.media-content-field-content { + position: relative; + display: inline-block; + width: auto; + padding-left: 126px; + + word-break: break-word; + + > * { + position: relative; + display: inline-block; + margin-right: 4px; + } + + font-size: 14px; + font-weight: 500; + line-height: 21px; + color: var(--body-text-color); + + a { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} + +.media-content-banner.large-fields-title .media-content-field-label { + width: 160px; +} + +.media-content-banner.large-fields-title .media-content-field-content { + padding-left: 160px; +} + +.share-embed-outer { position: absolute; top: 0; left: 0; right: 0; bottom: 0; - } -} - -.viewer-image-container { - position: relative; - display: block; - - img { - cursor: pointer; - position: relative; display: block; - max-width: 100%; - height: auto; - margin: 0 auto; - } -} - -.modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.8); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; -} - -.slideshow-container { - position: relative; - display: flex; - align-items: center; - justify-content: center; - width: auto; - max-width: 90%; -} - - - -.slideshow-image img { - display: block; - width: auto; - height: auto; - max-width: 100%; /* Ensure image doesn't exceed container width */ - max-height: 90vh; - border-radius: 0; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); - transition: transform 60s ease-in-out, opacity 60 ease-in-out; -} - -.slideshow-title { - margin-top: 10px; - text-align: start; - font-size: 16px; - font-weight: bold; - color: #bdb6b6; - z-index: 1200; -} - - -.arrow { - position: absolute; - display: flex; - justify-content: center; - align-items: center; - width: 40px; - height: 40px; - top: 50%; - transform: translateY(-50%); - border: none; - color: white; - font-size: 2rem; - background-color: rgba(0, 0, 0, 0.2); - cursor: pointer; - padding: 10px; - border-radius: 50%; - z-index: 1000; - transition: background-color 0.2s ease, transform 0.2s ease; - text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8); -} - -.arrow:hover { - background: rgba(92, 78, 78, 0.6); - transform: translateY(-50%) scale(1.1); -} - -.arrow.left { - left: 10px; -} - -.arrow.right { - right: 10px; -} - -.thumbnail-navigation { - position: fixed; - display: flex; - align-items: center; - justify-content: center; - margin-top: 20px; - gap: 10px; - bottom: 10%; - left: 50%; - transform: translateX(-50%); -} - -.thumbnail-container { - display: flex; - gap: 10px; - overflow-x: auto; - scroll-behavior: smooth; - max-width: 80%; - padding: 10px 0; - scrollbar-width: none; /* Hide scrollbar for Firefox */ -} - -.thumbnail-container.center-thumbnails { - display: flex; - justify-content: center; - overflow: visible; /* No scrollbars for fewer thumbnails */ -} - -.thumbnail-container::-webkit-scrollbar { - display: none; /* Hide scrollbar for Chrome/Safari */ -} - -.thumbnail { - width: 60px; - height: 60px; - object-fit: cover; - border: 2px solid transparent; - border-radius: 5px; - cursor: pointer; - transition: transform 0.3s ease; -} - -.thumbnail.active { - border-color: white; -} - -.thumbnail:hover { - transform: scale(1.1); -} - -.viewer-container .player-container { - @media screen and (min-width: 480px) { - border-radius: 10px; - } -} - -.viewer-container .player-container.audio-player-container { - @media screen and (min-width: 480px) { - padding-top: 0.75 * 56.25%; - } - - @media screen and (min-width: 640px) { - padding-top: 0.5 * 56.25%; - } - - .video-js.vjs-mediacms { - padding-top: 0; - } -} - -.viewer-container .pdf-container { - overflow-y: auto; - display: flex; - justify-content: center; - align-items: center; - width: 100%; // Default width for mobile - height: 400px; // Default height for mobile - - @media (min-width: 768px) and (max-width: 1023px) { // Tablets - width: 90%; - height: 600px; - } - - @media (min-width: 1024px) { // Desktop - width: 85%; - height: 900px; - } -} - - -.viewer-container .player-container.viewer-pdf-container, -.viewer-container .player-container.viewer-attachment-container { - background-color: var(--item-thumb-bg-color); - min-height: 0.5 * 259px; - padding-top: 0.25 * 56.25%; - - .player-container-inner { - .material-icons { - font-size: 4rem; - color: #888; - } - - > span { - position: absolute; - display: table; - top: 0; - left: 0; - width: 100%; - height: 100%; - - span { - display: table-cell; - text-align: center; - vertical-align: middle; - } - } - } -} - -.media-author-actions { - position: relative; - display: flex; - align-items: center; - gap: 12px; - font-family: inherit; - margin-bottom: -8px; - - button { - &:focus { - box-shadow: none; - } - } - - .edit-media-icon, - .remove-media-icon { - text-decoration: none; - color: #fff; - border: 0; - line-height: 1; - padding: 0; - width: 40px; - height: 40px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - - .material-icons { - font-size: 20px; - line-height: 1; - } - - &:active { - transform: scale(0.98); - } - } - - .edit-media-icon { - background-color: rgba(0, 153, 51, 0.9); - - &:hover { - background-color: rgba(0, 153, 51, 1); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); - transform: scale(1.05); - } - - .dark_theme & { - background-color: rgba(102, 187, 102, 0.9); - - &:hover { - background-color: rgba(102, 187, 102, 1); - } - } - } - - .remove-media-icon { - background-color: rgba(220, 53, 69, 0.9); - - &:hover { - background-color: rgba(220, 53, 69, 1); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); - transform: scale(1.05); - } - - .dark_theme & { - background-color: rgba(255, 107, 107, 0.9); - - &:hover { - background-color: rgba(255, 107, 107, 1); - } - } - } - - .popup { - position: absolute; - bottom: 100%; - left: 0; - margin-bottom: 8px; - } - - .popup-message-bottom { - button { - position: relative; - width: auto; - padding: 0; - border: 0; - background: none; - - &.proceed-comment-removal { - float: right; - } - - &.cancel-comment-removal { - float: left; - } - } - } -} - -.media-title-banner { - position: relative; - border-bottom-width: 1px; - border-bottom-style: solid; - - min-height: 64px; - - padding: 20px 12px 0 16px; - - @media screen and (min-width: 640px) { - padding: 20px 0 0; - } - - h1 { - display: inline-block; - font-size: 18px; - font-weight: 400; - line-height: 1.333333; - margin: 0; - - white-space: pre-wrap; - overflow-wrap: break-word; - } - - .media-labels-area { - position: relative; - display: block; - - .media-labels-area-inner { - display: table; - - > * { - display: table-cell; - vertical-align: middle; - } - } - - .media-label-state { - > * { - padding: 2px 4px; - margin-right: 4px; - font-size: 12px; - line-height: 1; - font-weight: 500; - text-transform: capitalize; - border-radius: 2px; - } - } - - .helper-icon { - padding: 0 4px 1px 0; - cursor: help; - - .material-icons { - display: inline-block; - padding: 0; - margin: 0; - font-size: 16px; - line-height: 1; - } - } - } - - .media-views-actions { - position: relative; - display: table; - width: 100%; - min-height: 40px; - z-index: 999; - - > * { - display: table-cell; - vertical-align: middle; - } - } - - .media-views { - line-height: 1.25; - font-family: inherit; - } - - .media-actions { - > * { - position: relative; - float: right; - - > * { - position: relative; - width: auto; - float: left; - margin-left: 8px; - padding-bottom: 8px; - color: rgb(144, 144, 144); - - button, - .circle-icon-button { - color: rgb(144, 144, 144); - - &:focus { - box-shadow: none; - } - } - - > *:not(.popup) { - display: table; - cursor: pointer; - - > * { - display: table-cell; - vertical-align: middle; - - &:nth-child(2) { - padding-right: 8px; - } - - @media screen and (max-width: 480px) { - &:nth-child(2) { - display: none !important; - } - } - } - - .circle-icon-button { - display: inline-block; - background-color: transparent; - - @media screen and (max-width: 359px) { - width: 2.25rem; - height: 2.25rem; - } - - @media screen and (max-width: 319px) { - width: 2rem; - height: 2rem; - } - - @media screen and (max-width: 299px) { - width: 1.75rem; - height: 1.75rem; - } - } - } - - .popup { - position: absolute; - top: 100%; - right: 0; - margin-top: -4px; - } - - &.like, - &.dislike, - &.share, - &.save, - &.download, - &.video-downloads { - > *:not(.popup) { - font-size: 13px; - font-weight: 500; - border: 0; - background: none; - - > * { - display: inline-block; - } - } - } - - &.like, - &.dislike, - &.more-options { - letter-spacing: 0.0007px; - } - - &.like, - &.dislike { - &:before { - content: ''; - position: absolute; - bottom: 0; - left: -4px; - right: -4px; - border-bottom: 2px solid; - } - } - - &.like { - } - - &.dislike { - } - - &.share { - .scrollable-content { - display: block; - padding-bottom: 8px; - overflow: auto; - } - - .share-popup-title { - margin-bottom: 16px; - line-height: 1.25; - } - - .share-options { - margin-bottom: 16px; - - .share-options-inner { - position: relative; - display: block; - white-space: nowrap; - overflow: hidden; - -webkit-overflow-scrolling: touch; - scroll-behavior: smooth; - } - - .previous-slide, - .next-slide { - position: absolute; - top: 78px; - z-index: +1; - - .circle-icon-button { - } - } - - .previous-slide { - left: 8px; - } - - .next-slide { - right: 8px; - } - - .sh-option { - vertical-align: top; - position: relative; - display: inline-block; - padding-right: 8px; - text-align: center; - - a, - button { - padding: 5px 5px 2px; - margin: 1px 0; - display: block; - text-decoration: none; - - outline: 0; - border: 0; - background: none; - - > *:first-child { - display: block; - width: 60px; - height: 60px; - line-height: 60px; - margin: 0 auto 8px; - border-radius: 50%; - background-position: center; - background-repeat: no-repeat; - } - - > *:last-child { - font-size: 13px; - line-height: 18px; - overflow: hidden; - } - - .material-icons { - padding: 0; - margin: 0 0 0 1px; - line-height: 1; - font-size: 30px; - overflow: hidden; - color: #fff; - } - } - } - - .share-embed-opt { - a, - button { - > *:first-child { - background-color: rgb(244, 244, 244); - } - - .material-icons { - color: rgb(111, 111, 111); - } - } - } - - .share-email { - a, - button { - > *:first-child { - background-color: rgb(136, 136, 136); - } - } - } - } - - .copy-field { - $button-width: 5.5rem; - - position: relative; - width: 100%; - - > div { - display: block; - padding-right: $button-width; - border-width: 1px; - border-style: solid; - border-radius: 2px; - } - - input[type='text'] { - width: 100%; - height: 42px; - padding: 1px 0 1px 16px; - font-family: Arial; - font-size: 14px; - line-height: normal; - border: 0; - background: none; - } - - button { - position: absolute; - top: 0; - right: 0; - width: $button-width; - height: 100%; - line-height: 20px; - border: 0; - background: none; - font-size: 14px; - font-weight: 500; - } - } - } - - &.share, - &.save, - &.download a { - color: rgb(144, 144, 144); - } - - &.more-options, - &.video-downloads { - .circle-icon-button { - outline-width: initial; - } - - .nav-page-main, - .nav-page-mediaStatusInfo, - .nav-page-videoDownloadOptions { - .popup { - width: auto; - overflow: visible; - box-shadow: none; - background-color: transparent; - - > * { - @media screen and (max-width: 1007px) { - width: 300px; - } - - @media screen and (min-width: 1008px) { - width: 368px; - } - - @media screen and (max-width: 480px) { - width: 220px; - } - - box-shadow: 0 16px 24px 2px rgba(#000, 0.14), 0 6px 30px 5px rgba(#000, 0.12), - 0 8px 10px -5px rgba(#000, 0.4); - - &.main-options, - &.video-download-options { - width: 240px; - box-shadow: 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12), 0 3px 1px -2px rgba(#000, 0.2); - } - } - } - } - - .nav-page-main, - .nav-page-videoDownloadOptions { - .popup { - > * { - &.main-options, - &.video-download-options { - width: 160px; - - .nav-menu { - padding: 8px 0; - - .menu-item-icon { - margin-right: 16px; - } - } - - .nav-menu a, - .nav-menu button { - padding: 0 16px; - font-size: 13px; - font-weight: 400; - - > * { - line-height: 48px; - } - } - } - } - } - } - } - - &.share, - &.save, - &.more-options, - &.video-downloads { - .popup-fullscreen { - .popup-main { - overflow: visible; - - > div { - position: relative; - - width: auto; - width: 100%; - max-width: 518px; - - display: inline-block; - margin: 0 auto; - padding: 24px; - text-align: initial; - - box-shadow: rgba(#000, 0.14) 0px 16px 24px 2px, rgba(#000, 0.12) 0px 6px 30px 5px, - rgba(#000, 0.4) 0px 8px 10px; - } - } - } - } - - &.video-downloads { - .circle-icon-button { - outline-width: initial; - } - - .nav-page-main, - .nav-page-videoDownloadOptions { - .popup { - width: auto; - overflow: visible; - box-shadow: none; - background-color: transparent; - - > * { - @media screen and (max-width: 1007px) { - width: 300px; - } - - @media screen and (min-width: 1008px) { - width: 368px; - } - - @media screen and (max-width: 480px) { - width: 220px; - } - - box-shadow: 0 16px 24px 2px rgba(#000, 0.14), 0 6px 30px 5px rgba(#000, 0.12), - 0 8px 10px -5px rgba(#000, 0.4); - - &.main-options, - &.video-download-options { - width: 240px; - box-shadow: 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12), 0 3px 1px -2px rgba(#000, 0.2); - - .popup-main { - min-height: 0; - max-height: 16em; - max-height: 33.333vh; - - overflow-x: hidden; - overflow-y: auto; - } - } - } - } - } - } - } - } - - .disliked-media { - > * { - > * { - &.dislike { - &:before { - } - } - } - } - } - } - - .media-views-actions { - &.liked-media { - .media-actions { - > * { - > * { - &.like, - &.like button, - &.like .circle-icon-button { - } - - &.like, - &.dislike { - &:before { - } - } - } - } - } - } - - &.disliked-media { - .media-actions { - > * { - > * { - &.dislike, - &.dislike button, - &.dislike .circle-icon-button { - } - - &.like, - &.dislike { - &:before { - } - } - } - } - } - } - } -} - -.nav-page-loggedInReportMedia .popup-main > div { - padding: 0 !important; - max-width: 450px !important; -} - -.report-form { - display: block; - overflow: auto; - max-height: 50%; - - .form-title { - padding: 20px 24px 0; - } - - .form-field { - padding: 24px 24px; - - + .form-field { - padding: 0 24px 24px 24px; - } - - position: relative; - display: block; - - .label { - display: block; - line-height: 1.5; - font-size: 13px; - margin-bottom: 8px; - font-weight: 500; - } - - input[type='text'], - textarea { - min-width: 100%; - width: 100%; - max-width: 100%; - height: 42px; - padding: 1px 12px; - margin: 0; - font-family: Arial; - font-size: 14px; - line-height: normal; - - border-width: 1px; - border-style: solid; - - border-radius: 2px; - } - - *[readonly] { - cursor: default; - cursor: not-allowed; - } - - input[type='text'] { - font-size: 14px; - } - - textarea { - line-height: 1.4; - padding: 8px 12px; - min-height: 80px; - min-height: 20vh; - max-height: 50vh; - } - } - - .form-help-text { - font-family: inherit; - font-size: 12px; - line-height: 15px; - margin-bottom: 8px; - } -} - -.form-actions-bottom { - display: block; - text-align: right; - padding: 8px; - border-top-width: 1px; - border-top-style: solid; - - button { - font-size: 14px; - font-stretch: 100%; - font-weight: 500; - line-height: 20px; - letter-spacing: 0.007px; - text-align: center; - padding: 10px 16px; - margin: 0; - border: 0; - background: none; - - &.cancel { - color: inherit !important; - } - } -} - -.media-info-content { - .media-author-banner { - display: table; - padding: 0 16px; - margin: 16px 0; - - @media screen and (min-width: 640px) { - padding: 0; - margin: 16px 0 10px; - } - - > * { - display: table-cell; - vertical-align: middle; - } - - .author-banner-thumb { - span { - display: block; - width: 48px; - height: 48px; - margin-right: 16px; - background-position: center; - background-repeat: no-repeat; - background-size: cover; - overflow: hidden; - border-radius: 50%; - - img { - width: 100%; - height: 100%; - opacity: 0; - } - } - } - - .author-banner-name { - font-size: 14px; - font-weight: 500; - line-height: 1; - text-decoration: none; - } - - .author-banner-date { - display: block; - font-size: 14px; - line-height: 1.384615; - font-family: inherit; - } - } - - .media-content-banner { - white-space: pre-wrap; - overflow-wrap: break-word; - - font-size: 14px; - line-height: 1.5; - - padding-bottom: 16px; - margin-bottom: 8px; - border-bottom-width: 1px; - border-bottom-style: solid; - - padding-left: 16px; - padding-right: 16px; - - margin-top: 20px; - - @media screen and (min-width: 640px) { - padding-right: 0; - padding-left: 0; - } - - @media screen and (min-width: 1008px) { - margin-bottom: 0; - border: 0; - } - - .media-content-banner-inner { - font-family: inherit; - - > * { - margin-bottom: 16px; - - &:last-child { - margin-bottom: 0; - } - } - - .load-more { - margin-bottom: 24px; - } - } - - .media-content-description { - p, - ul { - margin: 0; - } - } - } - - .media-author-banner + .media-content-banner { - margin-top: 0; - padding-top: 16px; - - border-top-width: 1px; - border-top-style: solid; - - @media screen and (min-width: 640px) { - padding-top: 0; - padding-left: 64px; - border-top-width: 0; - } - - @media screen and (min-width: 1008px) { - border: 0; - } - - .media-content-banner-inner { - max-width: 615px; - } - } -} - -.media-content-field { - position: relative; - width: 100%; - display: inline-block; - font-family: inherit; - line-height: 21px; - color: rgb(136, 136, 136); -} - -.media-content-field-label { - position: relative; - display: inline-block; - width: 126px; - padding-right: 16px; - - position: absolute; - top: 0; - left: 0; - - h4 { - margin: 0; - font-size: 14px; - font-weight: 400; - line-height: 21px; - } -} - -.media-content-field-content { - position: relative; - display: inline-block; - width: auto; - padding-left: 126px; - - word-break: break-word; - - > * { - position: relative; - display: inline-block; - margin-right: 4px; - } - - font-size: 14px; - font-weight: 500; - line-height: 21px; - color: var(--body-text-color); - - a { - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } -} - -.media-content-banner.large-fields-title .media-content-field-label { - width: 160px; -} - -.media-content-banner.large-fields-title .media-content-field-content { - padding-left: 160px; -} - -.share-embed-outer { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: block; - border: 2px solid red; + border: 2px solid red; } .share-embed { - overflow: auto; + overflow: auto; - .popup-main & { - padding: 0 !important; - max-width: 426px !important; + .popup-main & { + padding: 0 !important; + max-width: 426px !important; - @media screen and (min-width: 1157px) { - max-width: 1280px !important; + @media screen and (min-width: 1157px) { + max-width: 1280px !important; + } } - } } .share-embed-popup { - padding: 72px 0; + padding: 72px 0; - @media screen and (max-width: 1156px) { - } + @media screen and (max-width: 1156px) { + } - .popup-main { - padding-left: 16px; - padding-right: 16px; - } + .popup-main { + padding-left: 16px; + padding-right: 16px; + } } .share-embed-inner { - position: relative; - width: 100%; - float: left; - - .on-left, - .on-right { position: relative; - float: left; width: 100%; - } + float: left; - .on-left { - } - - .on-right { - overflow: hidden; - } - - .on-right-top, - .on-right-bottom { - border-style: solid; - } - - .on-right-top { - border-width: 0 0 1px; - - &:after { - content: ''; - position: absolute; - bottom: -5px; - right: 0; - width: 100%; - height: 5px; - left: 0; - opacity: 1; - pointer-events: none; - box-shadow: inset 0px 4px 8px -3px rgba(17, 17, 17, 0.06); + .on-left, + .on-right { + position: relative; + float: left; + width: 100%; } - .on-right-top-inner { - display: block; - padding: 16px; - - .ttl { - font-size: 16px; - line-height: 1.25; - } - - .circle-icon-button { - position: absolute; - top: 6px; - right: 8px; - } - } - } - - .on-right-middle { - position: absolute; - top: 60px; - bottom: 60px; - right: 0; - left: 0; - - padding: 16px; - word-break: break-word; - overflow: auto; - - @media screen and (max-width: 1156px) { - position: relative; - top: auto !important; - bottom: auto !important; - } - - textarea { - min-width: 314px; - width: 100%; - max-width: 100%; - min-height: 168px * 0.75; - min-height: 132px; - height: 152px; - max-height: 100%; - padding: 16px; - cursor: text; - font-family: 'Roboto Mono', monospace; - font-size: 14px; - line-height: 1.714285714; - outline: 0; - border-width: 1px; - border-style: solid; - border-radius: 2px; - } - - .iframe-config { - display: block; - padding: 16px 0 0; - } - - .iframe-config-options-title { - display: block; - padding: 0 0 16px; - - font-size: 13px; - font-weight: 500; - text-transform: uppercase; - } - - .iframe-config-option { - display: block; - color: var(--share-embed-inner-on-right-ttl-text-color); - - .option-content { - .options-group { - width: 50%; - display: inline-block; - vertical-align: top; - - input { - &[type='checkbox'] { - margin-left: 0; - } - } - } - - .ratio-options { - position: relative; - display: inline-block; - width: 100%; - - input { - &[type='checkbox'] { - margin-left: 0; - } - } - - select { - width: 167px; - max-width: 92%; - } - } - - .num-value-unit { - position: relative; - width: 100%; - display: inline-block; - - margin-bottom: 16px; - margin-right: 0; - - .value-input, - .value-unit { - } - - .value-input { - max-width: 56%; - } - - .value-unit { - } - } - } - } - } - - .on-right-bottom { - padding: 8px; - text-align: right; - border-width: 1px 0 0; - - button { - $button-width: 5.5rem; - padding: 10px 16px; - width: $button-width; - height: 100%; - line-height: 20px; - border: 0; - background: none; - font-size: 14px; - font-weight: 500; - } - } - - @media screen and (min-width: 1157px) { .on-left { - width: math.div(2, 3) * 100%; } .on-right { - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: math.div(1, 3) * 100%; + overflow: hidden; + } + + .on-right-top, + .on-right-bottom { + border-style: solid; + } + + .on-right-top { + border-width: 0 0 1px; + + &:after { + content: ""; + position: absolute; + bottom: -5px; + right: 0; + width: 100%; + height: 5px; + left: 0; + opacity: 1; + pointer-events: none; + box-shadow: inset 0px 4px 8px -3px rgba(17, 17, 17, 0.06); + } + + .on-right-top-inner { + display: block; + padding: 16px; + + .ttl { + font-size: 16px; + line-height: 1.25; + } + + .circle-icon-button { + position: absolute; + top: 6px; + right: 8px; + } + } + } + + .on-right-middle { + position: absolute; + top: 60px; + bottom: 60px; + right: 0; + left: 0; + + padding: 16px; + word-break: break-word; + overflow: auto; + + @media screen and (max-width: 1156px) { + position: relative; + top: auto !important; + bottom: auto !important; + } + + textarea { + min-width: 314px; + width: 100%; + max-width: 100%; + min-height: 168px * 0.75; + min-height: 132px; + height: 152px; + max-height: 100%; + padding: 16px; + cursor: text; + font-family: "Roboto Mono", monospace; + font-size: 14px; + line-height: 1.714285714; + outline: 0; + border-width: 1px; + border-style: solid; + border-radius: 2px; + } + + .iframe-config { + display: block; + padding: 16px 0 0; + } + + .iframe-config-options-title { + display: block; + padding: 0 0 16px; + + font-size: 13px; + font-weight: 500; + text-transform: uppercase; + } + + .iframe-config-option { + display: block; + color: var(--share-embed-inner-on-right-ttl-text-color); + + .option-content { + .options-group { + width: 50%; + display: inline-block; + vertical-align: top; + + input { + &[type="checkbox"] { + margin-left: 0; + } + } + } + + .ratio-options { + position: relative; + display: inline-block; + width: 100%; + + input { + &[type="checkbox"] { + margin-left: 0; + } + } + + select { + width: 167px; + max-width: 92%; + } + } + + .num-value-unit { + position: relative; + width: 100%; + display: inline-block; + + margin-bottom: 16px; + margin-right: 0; + + .value-input, + .value-unit { + } + + .value-input { + max-width: 56%; + } + + .value-unit { + } + } + } + } } .on-right-bottom { - position: absolute; - left: 0; - right: 0; + padding: 8px; + text-align: right; + border-width: 1px 0 0; + + button { + $button-width: 5.5rem; + padding: 10px 16px; + width: $button-width; + height: 100%; + line-height: 20px; + border: 0; + background: none; + font-size: 14px; + font-weight: 500; + } } - .on-right-bottom { - bottom: 0; - } - } + @media screen and (min-width: 1157px) { + .on-left { + width: math.div(2, 3) * 100%; + } - .media-embed-wrap { - display: block; + .on-right { + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: math.div(1, 3) * 100%; + } - .player-container, - .player-container-inner { - width: 100%; - height: 100%; + .on-right-bottom { + position: absolute; + left: 0; + right: 0; + } + + .on-right-bottom { + bottom: 0; + } } - button { - color: #fff; - } + .media-embed-wrap { + display: block; - .circle-icon-button { - } + .player-container, + .player-container-inner { + width: 100%; + height: 100%; + } - .video-js.vjs-mediacms { - padding-top: math.div(9, 16) * 100%; + button { + color: #fff; + } + + .circle-icon-button { + } + + .video-js.vjs-mediacms { + padding-top: math.div(9, 16) * 100%; + } } - } } .media-status-info { - list-style: none; - padding: 8px 0; - margin: 0; + list-style: none; + padding: 8px 0; + margin: 0; - li { - padding: 8px 20px; - font-size: 13px; + li { + padding: 8px 20px; + font-size: 13px; - span { - font-weight: 500; - text-transform: uppercase; + span { + font-weight: 500; + text-transform: uppercase; + } } - } - li.reports span { - color: red; - } + li.reports span { + color: red; + } } .loggedin-media-reported .menu-item-icon .material-icons { - color: red; - color: rgba(red, 0.8); + color: red; + color: rgba(red, 0.8); } @media screen and (min-width: 1008px) { - .viewer-sidebar, - .sliding-sidebar .viewer-sidebar, - .visible-sidebar .viewer-sidebar { - .item-thumb, - a.item-thumb { - width: 168px; - height: calc(0.5611 * 168px); - } - - .item-content { - padding-left: 168px; - } - - .item-meta { - > * { - &:nth-child(n + 2):before { - display: none; + .viewer-sidebar, + .sliding-sidebar .viewer-sidebar, + .visible-sidebar .viewer-sidebar { + .item-thumb, + a.item-thumb { + width: 168px; + height: calc(0.5611 * 168px); } - } - .item-date:before { - content: '•'; - content: '\2022'; - margin: 0 4px; - } + .item-content { + padding-left: 168px; + } - .item-author { - display: block; - } + .item-meta { + > * { + &:nth-child(n + 2):before { + display: none; + } + } + + .item-date:before { + content: "•"; + content: "\2022"; + margin: 0 4px; + } + + .item-author { + display: block; + } + } + + .item-main { + padding-left: 8px; + min-height: calc(0.5611 * 168px); + + h3 { + margin-top: 0; + margin-bottom: 4px; + font-size: var(--item-title-font-size); + font-weight: 500; + } + } } - - .item-main { - padding-left: 8px; - min-height: calc(0.5611 * 168px); - - h3 { - margin-top: 0; - margin-bottom: 4px; - font-size: var(--item-title-font-size); - font-weight: 500; - } - } - } } .media-under-title-categories { - margin: 16px 0; + margin: 16px 0; - &.over-title { - margin: 0; - } - - color: var(--item-meta-text-color); - - span { - display: inline-block; - margin-right: 4px; - - &:after { - content: ','; + &.over-title { + margin: 0; } - &:last-child { - margin-right: 0; + color: var(--item-meta-text-color); - &:after { - content: ''; - } + span { + display: inline-block; + margin-right: 4px; + + &:after { + content: ","; + } + + &:last-child { + margin-right: 0; + + &:after { + content: ""; + } + } } - } - a { - text-decoration: none; + a { + text-decoration: none; - &:hover { - text-decoration: underline; + &:hover { + text-decoration: underline; + } } - } } diff --git a/frontend/src/static/js/components/media-page/ViewerInfoContent.js b/frontend/src/static/js/components/media-page/ViewerInfoContent.js index 66c397d7..53fc1118 100755 --- a/frontend/src/static/js/components/media-page/ViewerInfoContent.js +++ b/frontend/src/static/js/components/media-page/ViewerInfoContent.js @@ -3,257 +3,278 @@ import { SiteContext } from '../../utils/contexts/'; import { useUser, usePopup } from '../../utils/hooks/'; import { PageStore, MediaPageStore } from '../../utils/stores/'; import { PageActions, MediaPageActions } from '../../utils/actions/'; -import { formatInnerLink, publishedOnDate } from '../../utils/helpers/'; +import { formatInnerLink, inEmbeddedApp, publishedOnDate } from '../../utils/helpers/'; import { PopupMain } from '../_shared/'; import CommentsList from '../comments/Comments'; import { replaceString } from '../../utils/helpers/'; import { translateString } from '../../utils/helpers/'; function metafield(arr) { - let i; - let sep; - let ret = []; + let i; + let sep; + let ret = []; - if (arr && arr.length) { - i = 0; - sep = 1 < arr.length ? ', ' : ''; - while (i < arr.length) { - ret[i] = ( -
- - {arr[i].title} - - {i < arr.length - 1 ? sep : ''} -
- ); - i += 1; + if (arr && arr.length) { + i = 0; + sep = 1 < arr.length ? ', ' : ''; + while (i < arr.length) { + ret[i] = ( +
+ + {arr[i].title} + + {i < arr.length - 1 ? sep : ''} +
+ ); + i += 1; + } } - } - return ret; + return ret; } function MediaAuthorBanner(props) { - return ( -
-
- - - {props.name} - - -
-
- - - {props.name} - - - {PageStore.get('config-media-item').displayPublishDate && props.published ? ( - - {translateString('Published on')} {replaceString(publishedOnDate(new Date(props.published)))} - - ) : null} -
-
- ); + return ( +
+
+ + + {props.name} + + +
+
+ + + {props.name} + + + {PageStore.get('config-media-item').displayPublishDate && props.published ? ( + + {translateString('Published on')} {replaceString(publishedOnDate(new Date(props.published)))} + + ) : null} +
+
+ ); } function MediaMetaField(props) { - return ( -
-
-
-

{props.title}

+ return ( +
+
+
+

{props.title}

+
+
{props.value}
+
-
{props.value}
-
-
- ); + ); } function EditMediaButton(props) { - let link = props.link; + let link = props.link; - if (window.MediaCMS.site.devEnv) { - link = '/edit-media.html'; - } + if (window.MediaCMS.site.devEnv) { + link = '/edit-media.html'; + } - return ( - - edit - - ); + return ( + + edit + + ); } export default function ViewerInfoContent(props) { - const { userCan } = useUser(); + const { userCan } = useUser(); - const description = props.description.trim(); - const tagsContent = - !PageStore.get('config-enabled').taxonomies.tags || PageStore.get('config-enabled').taxonomies.tags.enabled - ? metafield(MediaPageStore.get('media-tags')) - : []; - const categoriesContent = PageStore.get('config-options').pages.media.categoriesWithTitle - ? [] - : !PageStore.get('config-enabled').taxonomies.categories || - PageStore.get('config-enabled').taxonomies.categories.enabled - ? metafield(MediaPageStore.get('media-categories')) - : []; + const description = props.description.trim(); + const tagsContent = + !PageStore.get('config-enabled').taxonomies.tags || PageStore.get('config-enabled').taxonomies.tags.enabled + ? metafield(MediaPageStore.get('media-tags')) + : []; + const categoriesContent = PageStore.get('config-options').pages.media.categoriesWithTitle + ? [] + : !PageStore.get('config-enabled').taxonomies.categories || + PageStore.get('config-enabled').taxonomies.categories.enabled + ? metafield(MediaPageStore.get('media-categories')) + : []; - let summary = MediaPageStore.get('media-summary'); + let summary = MediaPageStore.get('media-summary'); - summary = summary ? summary.trim() : ''; + summary = summary ? summary.trim() : ''; - const [popupContentRef, PopupContent, PopupTrigger] = usePopup(); + const [popupContentRef, PopupContent, PopupTrigger] = usePopup(); - const [hasSummary, setHasSummary] = useState('' !== summary); - const [isContentVisible, setIsContentVisible] = useState('' == summary); + const [hasSummary, setHasSummary] = useState('' !== summary); + const [isContentVisible, setIsContentVisible] = useState('' == summary); - function proceedMediaRemoval() { - MediaPageActions.removeMedia(); - popupContentRef.current.toggle(); - } - - function cancelMediaRemoval() { - popupContentRef.current.toggle(); - } - - function onMediaDelete(mediaId) { - // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. - setTimeout(function () { - PageActions.addNotification('Media removed. Redirecting...', 'mediaDelete'); - setTimeout(function () { - window.location.href = - SiteContext._currentValue.url + '/' + MediaPageStore.get('media-data').author_profile.replace(/^\//g, ''); - }, 2000); - }, 100); - - if (void 0 !== mediaId) { - console.info("Removed media '" + mediaId + '"'); - } - } - - function onMediaDeleteFail(mediaId) { - // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. - setTimeout(function () { - PageActions.addNotification('Media removal failed', 'mediaDeleteFail'); - }, 100); - - if (void 0 !== mediaId) { - console.info('Media "' + mediaId + '"' + ' removal failed'); - } - } - - function onClickLoadMore() { - setIsContentVisible(!isContentVisible); - } - - useEffect(() => { - MediaPageStore.on('media_delete', onMediaDelete); - MediaPageStore.on('media_delete_fail', onMediaDeleteFail); - return () => { - MediaPageStore.removeListener('media_delete', onMediaDelete); - MediaPageStore.removeListener('media_delete_fail', onMediaDeleteFail); - }; - }, []); - - const authorLink = formatInnerLink(props.author.url, SiteContext._currentValue.url); - const authorThumb = formatInnerLink(props.author.thumb, SiteContext._currentValue.url); - - function setTimestampAnchors(text) { - function wrapTimestampWithAnchor(match, string) { - let split = match.split(':'), - s = 0, - m = 1; - - while (split.length > 0) { - s += m * parseInt(split.pop(), 10); - m *= 60; - } - - const wrapped = `${match}`; - return wrapped; + function proceedMediaRemoval() { + MediaPageActions.removeMedia(); + popupContentRef.current.toggle(); } - const timeRegex = new RegExp('((\\d)?\\d:)?(\\d)?\\d:\\d\\d', 'g'); - return text.replace(timeRegex, wrapTimestampWithAnchor); - } + function cancelMediaRemoval() { + popupContentRef.current.toggle(); + } - return ( -
- {void 0 === PageStore.get('config-media-item').displayAuthor || - null === PageStore.get('config-media-item').displayAuthor || - !!PageStore.get('config-media-item').displayAuthor ? ( - - ) : null} + function onMediaDelete(mediaId) { + // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. + setTimeout(function () { + PageActions.addNotification('Media removed. Redirecting...', 'mediaDelete'); + setTimeout(function () { + window.location.href = + SiteContext._currentValue.url + + '/' + + MediaPageStore.get('media-data').author_profile.replace(/^\//g, ''); + }, 2000); + }, 100); -
-
- {hasSummary ?
{summary}
: null} - {(!hasSummary || isContentVisible) && description ? ( -
- ) : null} - {hasSummary ? ( - - ) : null} - {tagsContent.length ? ( - - ) : null} - {categoriesContent.length ? ( - - ) : null} + if (void 0 !== mediaId) { + console.info("Removed media '" + mediaId + '"'); + } + } - {userCan.editMedia ? ( -
- {userCan.editMedia ? : null} + function onMediaDeleteFail(mediaId) { + // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. + setTimeout(function () { + PageActions.addNotification('Media removal failed', 'mediaDeleteFail'); + }, 100); - {userCan.deleteMedia ? ( - - - - ) : null} + if (void 0 !== mediaId) { + console.info('Media "' + mediaId + '"' + ' removal failed'); + } + } - {userCan.deleteMedia ? ( - - -
- Media removal - You're willing to remove media permanently? -
-
- - - - -
-
- ) : null} + function onClickLoadMore() { + setIsContentVisible(!isContentVisible); + } + + useEffect(() => { + MediaPageStore.on('media_delete', onMediaDelete); + MediaPageStore.on('media_delete_fail', onMediaDeleteFail); + return () => { + MediaPageStore.removeListener('media_delete', onMediaDelete); + MediaPageStore.removeListener('media_delete_fail', onMediaDeleteFail); + }; + }, []); + + const authorLink = formatInnerLink(props.author.url, SiteContext._currentValue.url); + const authorThumb = formatInnerLink(props.author.thumb, SiteContext._currentValue.url); + + function setTimestampAnchors(text) { + function wrapTimestampWithAnchor(match, string) { + let split = match.split(':'), + s = 0, + m = 1; + + while (split.length > 0) { + s += m * parseInt(split.pop(), 10); + m *= 60; + } + + const wrapped = `${match}`; + return wrapped; + } + + const timeRegex = new RegExp('((\\d)?\\d:)?(\\d)?\\d:\\d\\d', 'g'); + return text.replace(timeRegex, wrapTimestampWithAnchor); + } + + return ( +
+ {void 0 === PageStore.get('config-media-item').displayAuthor || + null === PageStore.get('config-media-item').displayAuthor || + !!PageStore.get('config-media-item').displayAuthor ? ( + + ) : null} + +
+
+ {hasSummary ?
{summary}
: null} + {(!hasSummary || isContentVisible) && description ? ( +
+ ) : null} + {hasSummary ? ( + + ) : null} + {tagsContent.length ? ( + + ) : null} + {categoriesContent.length ? ( + + ) : null} + + {userCan.editMedia ? ( +
+ {userCan.editMedia ? ( + + ) : null} + + {userCan.deleteMedia ? ( + + + + ) : null} + + {userCan.deleteMedia ? ( + + +
+ Media removal + + You're willing to remove media permanently? + +
+
+ + + + +
+
+ ) : null} +
+ ) : null} +
- ) : null} -
-
- -
- ); + {!inEmbeddedApp() && } +
+ ); } diff --git a/frontend/src/static/js/components/page-layout/PageMain.scss b/frontend/src/static/js/components/page-layout/PageMain.scss index 55c258c8..3e81c203 100755 --- a/frontend/src/static/js/components/page-layout/PageMain.scss +++ b/frontend/src/static/js/components/page-layout/PageMain.scss @@ -1,28 +1,33 @@ .page-main-wrap { - padding-top: var(--header-height); - will-change: padding-left; + padding-top: var(--header-height); + will-change: padding-left; + + @media (min-width: 768px) { + .visible-sidebar & { + padding-left: var(--sidebar-width); + opacity: 1; + } + } + + .visible-sidebar #page-media & { + padding-left: 0; + } - @media (min-width: 768px) { .visible-sidebar & { - padding-left: var(--sidebar-width); - opacity: 1; + #page-media { + padding-left: 0; + } } - } - .visible-sidebar #page-media & { - padding-left: 0; - } - - .visible-sidebar & { - #page-media { - padding-left: 0; + body.sliding-sidebar & { + transition-property: padding-left; + transition-duration: 0.2s; } - } - body.sliding-sidebar & { - transition-property: padding-left; - transition-duration: 0.2s; - } + .embedded-app & { + padding-top: 0; + padding-left: 0; + } } #page-profile-media, @@ -30,20 +35,20 @@ #page-profile-about, #page-liked.profile-page-liked, #page-history.profile-page-history { - .page-main { - min-height: calc(100vh - var(--header-height)); - } + .page-main { + min-height: calc(100vh - var(--header-height)); + } } .page-main { - position: relative; - width: 100%; - padding-bottom: 16px; + position: relative; + width: 100%; + padding-bottom: 16px; } .page-main-inner { - display: block; - margin: 1em 1em 0 1em; + display: block; + margin: 1em 1em 0 1em; } #page-profile-media, @@ -51,7 +56,7 @@ #page-profile-about, #page-liked.profile-page-liked, #page-history.profile-page-history { - .page-main-wrap { - background-color: var(--body-bg-color); - } + .page-main-wrap { + background-color: var(--body-bg-color); + } } diff --git a/frontend/src/static/js/components/profile-page/ProfilePagesHeader.js b/frontend/src/static/js/components/profile-page/ProfilePagesHeader.js index d114c2d5..be5fce04 100644 --- a/frontend/src/static/js/components/profile-page/ProfilePagesHeader.js +++ b/frontend/src/static/js/components/profile-page/ProfilePagesHeader.js @@ -8,78 +8,78 @@ import { CircleIconButton, PopupMain } from '../_shared'; import { translateString } from '../../utils/helpers/'; class ProfileSearchBar extends React.PureComponent { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - visibleForm: false, - queryVal: ProfilePageStore.get('author-query') || '', - }; + this.state = { + visibleForm: false, + queryVal: ProfilePageStore.get('author-query') || '', + }; - this.onChange = this.onChange.bind(this); - this.onInputFocus = this.onInputFocus.bind(this); - this.onInputBlur = this.onInputBlur.bind(this); - this.showForm = this.showForm.bind(this); - this.hideForm = this.hideForm.bind(this); - this.onFormSubmit = this.onFormSubmit.bind(this); + this.onChange = this.onChange.bind(this); + this.onInputFocus = this.onInputFocus.bind(this); + this.onInputBlur = this.onInputBlur.bind(this); + this.showForm = this.showForm.bind(this); + this.hideForm = this.hideForm.bind(this); + this.onFormSubmit = this.onFormSubmit.bind(this); - this.updateTimeout = null; - this.pendingUpdate = false; - this.justShown = false; - } + this.updateTimeout = null; + this.pendingUpdate = false; + this.justShown = false; + } - updateQuery(value) { - this.pendingUpdateValue = null; + updateQuery(value) { + this.pendingUpdateValue = null; - this.setState( - { - queryVal: value, - }, - function () { - if ('function' === typeof this.props.onQueryChange) { - this.props.onQueryChange(this.state.queryVal); - } - } - ); - } - - onChange(ev) { - this.pendingEvent = ev; - const newValue = ev.target.value || ''; - - this.setState( - { - queryVal: newValue, - }, - function () { - if (this.updateTimeout) { - return; - } - - this.pendingEvent = null; - - // Only trigger search if 3+ characters or empty (to reset) - if ('function' === typeof this.props.onQueryChange) { - if (newValue.length >= 3 || newValue.length === 0) { - this.props.onQueryChange(newValue); - } - } - - this.updateTimeout = setTimeout( - function () { - this.updateTimeout = null; - - if (this.pendingEvent) { - this.onChange(this.pendingEvent); + this.setState( + { + queryVal: value, + }, + function () { + if ('function' === typeof this.props.onQueryChange) { + this.props.onQueryChange(this.state.queryVal); + } } - }.bind(this), - 100 ); - } - ); - } + } - /*onKeydown(e){ + onChange(ev) { + this.pendingEvent = ev; + const newValue = ev.target.value || ''; + + this.setState( + { + queryVal: newValue, + }, + function () { + if (this.updateTimeout) { + return; + } + + this.pendingEvent = null; + + // Only trigger search if 3+ characters or empty (to reset) + if ('function' === typeof this.props.onQueryChange) { + if (newValue.length >= 3 || newValue.length === 0) { + this.props.onQueryChange(newValue); + } + } + + this.updateTimeout = setTimeout( + function () { + this.updateTimeout = null; + + if (this.pendingEvent) { + this.onChange(this.pendingEvent); + } + }.bind(this), + 100 + ); + } + ); + } + + /*onKeydown(e){ let found = false, key = e.keyCode || e.charCode; switch( key ){ case 38: // Arrow Up. @@ -96,665 +96,726 @@ class ProfileSearchBar extends React.PureComponent { } }*/ - onInputFocus() { - // console.log('FOCUS'); - /*if( this.state.predictionItems.length ){ + onInputFocus() { + // console.log('FOCUS'); + /*if( this.state.predictionItems.length ){ this.refs.SearchInput.onkeydown = this.refs.SearchInput.onkeydown || this.onKeydown; }*/ - } - - onInputBlur() { - // Don't hide immediately after showing to prevent race condition - if (this.justShown) { - return; } - this.hideForm(); - } - showForm() { - this.justShown = true; - this.setState( - { - visibleForm: true, - }, - function () { - if ('function' === typeof this.props.toggleSearchField) { - this.props.toggleSearchField(); + onInputBlur() { + // Don't hide immediately after showing to prevent race condition + if (this.justShown) { + return; } - // Reset the flag after a short delay - setTimeout(() => { - this.justShown = false; - }, 200); - } - ); - } + this.hideForm(); + } - hideForm() { - this.setState( - { - visibleForm: false, - }, - function () { - if ('function' === typeof this.props.toggleSearchField) { - this.props.toggleSearchField(); + showForm() { + this.justShown = true; + this.setState( + { + visibleForm: true, + }, + function () { + if ('function' === typeof this.props.toggleSearchField) { + this.props.toggleSearchField(); + } + // Reset the flag after a short delay + setTimeout(() => { + this.justShown = false; + }, 200); + } + ); + } + + hideForm() { + this.setState( + { + visibleForm: false, + }, + function () { + if ('function' === typeof this.props.toggleSearchField) { + this.props.toggleSearchField(); + } + } + ); + } + + onFormSubmit(ev) { + if ('' === this.refs.SearchInput.value.trim()) { + ev.preventDefault(); + ev.stopPropagation(); } - } - ); - } - - onFormSubmit(ev) { - if ('' === this.refs.SearchInput.value.trim()) { - ev.preventDefault(); - ev.stopPropagation(); - } - } - - render() { - const hasSearchText = this.state.queryVal && this.state.queryVal.length > 0; - - // Determine the correct action URL based on page type - let actionUrl = LinksContext._currentValue.profile.media; - if (this.props.type === 'shared_by_me') { - actionUrl = LinksContext._currentValue.profile.shared_by_me; - } else if (this.props.type === 'shared_with_me') { - actionUrl = LinksContext._currentValue.profile.shared_with_me; } - if (!this.state.visibleForm) { - return ( - - - search - - {hasSearchText ? ( - - ) : null} - - ); - } + render() { + const hasSearchText = this.state.queryVal && this.state.queryVal.length > 0; - return ( -
- - - search - - {hasSearchText ? ( - - ) : null} - - - - -
- ); - } + // Determine the correct action URL based on page type + let actionUrl = LinksContext._currentValue.profile.media; + if (this.props.type === 'shared_by_me') { + actionUrl = LinksContext._currentValue.profile.shared_by_me; + } else if (this.props.type === 'shared_with_me') { + actionUrl = LinksContext._currentValue.profile.shared_with_me; + } + + if (!this.state.visibleForm) { + return ( + + + search + + {hasSearchText ? ( + + ) : null} + + ); + } + + return ( +
+ + + search + + {hasSearchText ? ( + + ) : null} + + + + +
+ ); + } } ProfileSearchBar.propTypes = { - onQueryChange: PropTypes.func, - type: PropTypes.string, + onQueryChange: PropTypes.func, + type: PropTypes.string, }; ProfileSearchBar.defaultProps = {}; function InlineTab(props) { - return ( -
  • - - {props.label} - -
  • - ); + return ( +
  • + + {props.label} + +
  • + ); } InlineTab.propTypes = { - id: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - link: PropTypes.string.isRequired, - isActive: PropTypes.bool.isRequired, + id: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + link: PropTypes.string.isRequired, + isActive: PropTypes.bool.isRequired, }; class NavMenuInlineTabs extends React.PureComponent { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - displayNext: false, - displayPrev: false, - }; + this.state = { + displayNext: false, + displayPrev: false, + }; - this.nextSlide = this.nextSlide.bind(this); - this.prevSlide = this.prevSlide.bind(this); + this.nextSlide = this.nextSlide.bind(this); + this.prevSlide = this.prevSlide.bind(this); - this.updateSlider = this.updateSlider.bind(this); - this.updateSliderButtonsView = this.updateSliderButtonsView.bind(this); + this.updateSlider = this.updateSlider.bind(this); + this.updateSliderButtonsView = this.updateSliderButtonsView.bind(this); - this.onToggleSearchField = this.onToggleSearchField.bind(this); + this.onToggleSearchField = this.onToggleSearchField.bind(this); - PageStore.on('window_resize', this.updateSlider); + PageStore.on('window_resize', this.updateSlider); - this.sliderRecalTimeout = null; + this.sliderRecalTimeout = null; - PageStore.on( - 'changed_page_sidebar_visibility', - function () { - clearTimeout(this.sliderRecalTimeout); + PageStore.on( + 'changed_page_sidebar_visibility', + function () { + clearTimeout(this.sliderRecalTimeout); - // NOTE: 200ms is transition duration, set in CSS. - this.sliderRecalTimeout = setTimeout( - function () { - this.updateSliderButtonsView(); + // NOTE: 200ms is transition duration, set in CSS. + this.sliderRecalTimeout = setTimeout( + function () { + this.updateSliderButtonsView(); - this.sliderRecalTimeout = setTimeout( - function () { - this.sliderRecalTimeout = null; + this.sliderRecalTimeout = setTimeout( + function () { + this.sliderRecalTimeout = null; - this.updateSlider(); - }.bind(this), - 50 - ); - }.bind(this), - 150 + this.updateSlider(); + }.bind(this), + 50 + ); + }.bind(this), + 150 + ); + }.bind(this) ); - }.bind(this) - ); - this.previousBtn = ( - - - keyboard_arrow_left - - - ); - this.nextBtn = ( - - - keyboard_arrow_right - - - ); + this.previousBtn = ( + + + keyboard_arrow_left + + + ); + this.nextBtn = ( + + + keyboard_arrow_right + + + ); - this.userIsAuthor = - !MemberContext._currentValue.is.anonymous && - ProfilePageStore.get('author-data').username === MemberContext._currentValue.username; - } - - componentDidMount() { - this.updateSlider(); - if (this.refs.itemsListWrap) { - this.refs.itemsListWrap.addEventListener('scroll', this.updateSliderButtonsView.bind(this)); + this.userIsAuthor = + !MemberContext._currentValue.is.anonymous && + ProfilePageStore.get('author-data').username === MemberContext._currentValue.username; } - } - componentWillUnmount() { - if (this.refs.itemsListWrap) { - this.refs.itemsListWrap.removeEventListener('scroll', this.updateSliderButtonsView.bind(this)); + componentDidMount() { + this.updateSlider(); + if (this.refs.itemsListWrap) { + this.refs.itemsListWrap.addEventListener('scroll', this.updateSliderButtonsView.bind(this)); + } } - } - nextSlide() { - if (!this.refs.itemsListWrap) return; - const scrollAmount = this.refs.itemsListWrap.offsetWidth * 0.7; // Scroll 70% of visible width - this.refs.itemsListWrap.scrollLeft += scrollAmount; - setTimeout(() => this.updateSliderButtonsView(), 50); - } + componentWillUnmount() { + if (this.refs.itemsListWrap) { + this.refs.itemsListWrap.removeEventListener('scroll', this.updateSliderButtonsView.bind(this)); + } + } - prevSlide() { - if (!this.refs.itemsListWrap) return; - const scrollAmount = this.refs.itemsListWrap.offsetWidth * 0.7; // Scroll 70% of visible width - this.refs.itemsListWrap.scrollLeft -= scrollAmount; - setTimeout(() => this.updateSliderButtonsView(), 50); - } + nextSlide() { + if (!this.refs.itemsListWrap) return; + const scrollAmount = this.refs.itemsListWrap.offsetWidth * 0.7; // Scroll 70% of visible width + this.refs.itemsListWrap.scrollLeft += scrollAmount; + setTimeout(() => this.updateSliderButtonsView(), 50); + } - updateSlider(afterItemsUpdate) { - this.updateSliderButtonsView(); - } + prevSlide() { + if (!this.refs.itemsListWrap) return; + const scrollAmount = this.refs.itemsListWrap.offsetWidth * 0.7; // Scroll 70% of visible width + this.refs.itemsListWrap.scrollLeft -= scrollAmount; + setTimeout(() => this.updateSliderButtonsView(), 50); + } - updateSliderButtonsView() { - if (!this.refs.itemsListWrap) return; + updateSlider(afterItemsUpdate) { + this.updateSliderButtonsView(); + } - const container = this.refs.itemsListWrap; - const scrollLeft = container.scrollLeft; - const scrollWidth = container.scrollWidth; - const clientWidth = container.clientWidth; + updateSliderButtonsView() { + if (!this.refs.itemsListWrap) return; - // Show prev arrow if we can scroll left - const canScrollLeft = scrollLeft > 1; + const container = this.refs.itemsListWrap; + const scrollLeft = container.scrollLeft; + const scrollWidth = container.scrollWidth; + const clientWidth = container.clientWidth; - // Show next arrow if we can scroll right - const canScrollRight = scrollLeft < scrollWidth - clientWidth - 1; + // Show prev arrow if we can scroll left + const canScrollLeft = scrollLeft > 1; - this.setState({ - displayPrev: canScrollLeft, - displayNext: canScrollRight, - }); - } + // Show next arrow if we can scroll right + const canScrollRight = scrollLeft < scrollWidth - clientWidth - 1; - onToggleSearchField() { - setTimeout(() => this.updateSlider(), 100); - } + this.setState({ + displayPrev: canScrollLeft, + displayNext: canScrollRight, + }); + } - render() { - return ( - + ); + } } NavMenuInlineTabs.propTypes = { - type: PropTypes.string.isRequired, - onQueryChange: PropTypes.func, - onToggleFiltersClick: PropTypes.func, - onToggleTagsClick: PropTypes.func, - onToggleSortingClick: PropTypes.func, - hasActiveFilters: PropTypes.bool, - hasActiveTags: PropTypes.bool, - hasActiveSort: PropTypes.bool, + type: PropTypes.string.isRequired, + onQueryChange: PropTypes.func, + onToggleFiltersClick: PropTypes.func, + onToggleTagsClick: PropTypes.func, + onToggleSortingClick: PropTypes.func, + hasActiveFilters: PropTypes.bool, + hasActiveTags: PropTypes.bool, + hasActiveSort: PropTypes.bool, }; function AddBannerButton(props) { - let link = props.link; + let link = props.link; - if (window.MediaCMS.site.devEnv) { - link = '/edit-channel.html'; - } - return ( - - add_photo_alternate - - ); + if (window.MediaCMS.site.devEnv) { + link = '/edit-channel.html'; + } + return ( + + add_photo_alternate + + ); } function EditBannerButton(props) { - let link = props.link; + let link = props.link; - if (window.MediaCMS.site.devEnv) { - link = '/edit-channel.html'; - } - return ( - - edit - - ); + if (window.MediaCMS.site.devEnv) { + link = '/edit-channel.html'; + } + return ( + + edit + + ); } function EditProfileButton(props) { - let link = props.link; + let link = props.link; - if (window.MediaCMS.site.devEnv) { - link = '/edit-profile.html'; - } + if (window.MediaCMS.site.devEnv) { + link = '/edit-profile.html'; + } - return ( - - edit - - ); + return ( + + edit + + ); } export default function ProfilePagesHeader(props) { - const [popupContentRef, PopupContent, PopupTrigger] = usePopup(); + const [popupContentRef, PopupContent, PopupTrigger] = usePopup(); - const profilePageHeaderRef = useRef(null); - const profileNavRef = useRef(null); + const profilePageHeaderRef = useRef(null); + const profileNavRef = useRef(null); - const [fixedNav, setFixedNav] = useState(false); + const [fixedNav, setFixedNav] = useState(false); - const positions = { - profileNavTop: 0, - }; - - const userIsAdmin = !MemberContext._currentValue.is.anonymous && MemberContext._currentValue.is.admin; - const userIsAuthor = - !MemberContext._currentValue.is.anonymous && - ProfilePageStore.get('author-data').username === MemberContext._currentValue.username; - const userCanEditProfile = - userIsAuthor || (!MemberContext._currentValue.is.anonymous && MemberContext._currentValue.can.editProfile); - const userCanDeleteProfile = - userIsAdmin || - userIsAuthor || - (!MemberContext._currentValue.is.anonymous && MemberContext._currentValue.can.deleteProfile); - - function updateProfileNavTopPosition() { - positions.profileHeaderTop = profilePageHeaderRef.current.offsetTop; - positions.profileNavTop = - positions.profileHeaderTop + - profilePageHeaderRef.current.offsetHeight - - profileNavRef.current.refs.tabsNav.offsetHeight; - } - - function updateFixedNavPosition() { - setFixedNav(positions.profileHeaderTop + window.scrollY > positions.profileNavTop); - } - - function cancelProfileRemoval() { - popupContentRef.current.toggle(); - } - - function proceedMediaRemoval() { - ProfilePageActions.remove_profile(); - popupContentRef.current.toggle(); - } - - function onProfileDelete(username) { - // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. - setTimeout(function () { - PageActions.addNotification('Profile removed. Redirecting...', 'profileDelete'); - setTimeout(function () { - window.location.href = SiteContext._currentValue.url; - }, 2000); - }, 100); - - if (void 0 !== username) { - console.info("Removed user's profile '" + username + '"'); - } - } - - function onProfileDeleteFail(username) { - // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. - setTimeout(function () { - PageActions.addNotification('Profile removal failed', 'profileDeleteFail'); - }, 100); - - if (void 0 !== username) { - console.info('Profile "' + username + '"' + ' removal failed'); - } - } - - function onWindowResize() { - updateProfileNavTopPosition(); - updateFixedNavPosition(); - } - - function onWindowScroll() { - updateFixedNavPosition(); - } - - useEffect(() => { - if (userCanDeleteProfile) { - ProfilePageStore.on('profile_delete', onProfileDelete); - ProfilePageStore.on('profile_delete_fail', onProfileDeleteFail); - } - - PageStore.on('resize', onWindowResize); - PageStore.on('changed_page_sidebar_visibility', onWindowResize); - PageStore.on('window_scroll', onWindowScroll); - - updateProfileNavTopPosition(); - updateFixedNavPosition(); - - return () => { - if (userCanDeleteProfile) { - ProfilePageStore.removeListener('profile_delete', onProfileDelete); - ProfilePageStore.removeListener('profile_delete_fail', onProfileDeleteFail); - } - - PageStore.removeListener('resize', onWindowResize); - PageStore.removeListener('changed_page_sidebar_visibility', onWindowResize); - PageStore.removeListener('window_scroll', onWindowScroll); + const positions = { + profileNavTop: 0, }; - }, []); - return ( -
    - - {props.author.banner_thumbnail_url ? ( - - ) : null} + const userIsAdmin = !MemberContext._currentValue.is.anonymous && MemberContext._currentValue.is.admin; + const userIsAuthor = + !MemberContext._currentValue.is.anonymous && + ProfilePageStore.get('author-data').username === MemberContext._currentValue.username; + const userCanEditProfile = + userIsAuthor || (!MemberContext._currentValue.is.anonymous && MemberContext._currentValue.can.editProfile); + const userCanDeleteProfile = + userIsAdmin || + userIsAuthor || + (!MemberContext._currentValue.is.anonymous && MemberContext._currentValue.can.deleteProfile); - {userCanDeleteProfile && !userIsAuthor ? ( - - - - + function updateProfileNavTopPosition() { + positions.profileHeaderTop = profilePageHeaderRef.current.offsetTop; + positions.profileNavTop = + positions.profileHeaderTop + + profilePageHeaderRef.current.offsetHeight - + profileNavRef.current.refs.tabsNav.offsetHeight; + } - - -
    - Profile removal - You're willing to remove profile permanently? -
    -
    - - - + function updateFixedNavPosition() { + setFixedNav(positions.profileHeaderTop + window.scrollY > positions.profileNavTop); + } + + function cancelProfileRemoval() { + popupContentRef.current.toggle(); + } + + function proceedMediaRemoval() { + ProfilePageActions.remove_profile(); + popupContentRef.current.toggle(); + } + + function onProfileDelete(username) { + // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. + setTimeout(function () { + PageActions.addNotification('Profile removed. Redirecting...', 'profileDelete'); + setTimeout(function () { + window.location.href = SiteContext._currentValue.url; + }, 2000); + }, 100); + + if (void 0 !== username) { + console.info("Removed user's profile '" + username + '"'); + } + } + + function onProfileDeleteFail(username) { + // FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ]. + setTimeout(function () { + PageActions.addNotification('Profile removal failed', 'profileDeleteFail'); + }, 100); + + if (void 0 !== username) { + console.info('Profile "' + username + '"' + ' removal failed'); + } + } + + function onWindowResize() { + updateProfileNavTopPosition(); + updateFixedNavPosition(); + } + + function onWindowScroll() { + updateFixedNavPosition(); + } + + useEffect(() => { + if (userCanDeleteProfile) { + ProfilePageStore.on('profile_delete', onProfileDelete); + ProfilePageStore.on('profile_delete_fail', onProfileDeleteFail); + } + + PageStore.on('resize', onWindowResize); + PageStore.on('changed_page_sidebar_visibility', onWindowResize); + PageStore.on('window_scroll', onWindowScroll); + + updateProfileNavTopPosition(); + updateFixedNavPosition(); + + return () => { + if (userCanDeleteProfile) { + ProfilePageStore.removeListener('profile_delete', onProfileDelete); + ProfilePageStore.removeListener('profile_delete_fail', onProfileDeleteFail); + } + + PageStore.removeListener('resize', onWindowResize); + PageStore.removeListener('changed_page_sidebar_visibility', onWindowResize); + PageStore.removeListener('window_scroll', onWindowScroll); + }; + }, []); + + return ( +
    + {!props.hideChannelBanner && ( + + {props.author.banner_thumbnail_url ? ( + + ) : null} + + {userCanDeleteProfile && !userIsAuthor ? ( + + + + + + + +
    + Profile removal + + You're willing to remove profile permanently? + +
    +
    + + + + +
    +
    +
    + ) : null} + + {userCanEditProfile && userIsAuthor ? ( + props.author.banner_thumbnail_url ? ( + + ) : ( + + ) + ) : null}
    - - - - ) : null} + )} - {userCanEditProfile && userIsAuthor ? ( - props.author.banner_thumbnail_url ? ( - - ) : ( - - ) - ) : null} - - -
    - {props.author.thumbnail_url || props.author.name ? ( -
    -
    -
    {props.author.thumbnail_url ? : null}
    -
    - {props.author.name ? ( -
    -

    {props.author.name}

    - {userCanEditProfile && !userIsAuthor ? : null} -
    +
    + {props.author.thumbnail_url || props.author.name ? ( +
    +
    +
    + {props.author.thumbnail_url ? : null} +
    +
    + {props.author.name ? ( +
    +

    {props.author.name}

    + {userCanEditProfile && !userIsAuthor ? ( + + ) : null} +
    + ) : null} +
    +
    +
    ) : null} -
    -
    -
    - ) : null} - -
    -
    - ); + +
    +
    + ); } ProfilePagesHeader.propTypes = { - author: PropTypes.object.isRequired, - type: PropTypes.string.isRequired, - onQueryChange: PropTypes.func, - onToggleFiltersClick: PropTypes.func, - onToggleTagsClick: PropTypes.func, - onToggleSortingClick: PropTypes.func, - hasActiveFilters: PropTypes.bool, - hasActiveTags: PropTypes.bool, - hasActiveSort: PropTypes.bool, + author: PropTypes.object.isRequired, + type: PropTypes.string.isRequired, + onQueryChange: PropTypes.func, + onToggleFiltersClick: PropTypes.func, + onToggleTagsClick: PropTypes.func, + onToggleSortingClick: PropTypes.func, + hasActiveFilters: PropTypes.bool, + hasActiveTags: PropTypes.bool, + hasActiveSort: PropTypes.bool, }; ProfilePagesHeader.defaultProps = { - type: 'media', + type: 'media', }; diff --git a/frontend/src/static/js/pages/ProfileMediaPage.js b/frontend/src/static/js/pages/ProfileMediaPage.js index eabe8d6b..7a8797b6 100755 --- a/frontend/src/static/js/pages/ProfileMediaPage.js +++ b/frontend/src/static/js/pages/ProfileMediaPage.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { ApiUrlContext, LinksConsumer, MemberContext } from '../utils/contexts'; import { PageStore, ProfilePageStore } from '../utils/stores'; import { ProfilePageActions, PageActions } from '../utils/actions'; -import { translateString } from '../utils/helpers/'; +import { inEmbeddedApp, translateString } from '../utils/helpers/'; import { MediaListWrapper } from '../components/MediaListWrapper'; import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader'; import ProfilePagesContent from '../components/profile-page/ProfilePagesContent'; @@ -24,994 +24,1062 @@ import { Page } from './_Page'; import '../components/profile-page/ProfilePage.scss'; export class ProfileMediaPage extends Page { - constructor(props, pageSlug) { - super(props, 'string' === typeof pageSlug ? pageSlug : 'author-home'); + constructor(props, pageSlug) { + super(props, 'string' === typeof pageSlug ? pageSlug : 'author-home'); - this.profilePageSlug = 'string' === typeof pageSlug ? pageSlug : 'author-home'; + this.profilePageSlug = 'string' === typeof pageSlug ? pageSlug : 'author-home'; - this.state = { - channelMediaCount: -1, - author: ProfilePageStore.get('author-data'), - uploadsPreviewItemsCount: 0, - title: this.props.title, - query: ProfilePageStore.get('author-query'), - requestUrl: null, - selectedMedia: new Set(), - availableMediaIds: [], - showConfirmModal: false, - pendingAction: null, - confirmMessage: '', - listKey: 0, - notificationMessage: '', - showNotification: false, - notificationType: 'success', - hiddenFilters: true, - hiddenTags: true, - hiddenSorting: true, - filterArgs: '', - availableTags: [], - selectedTag: 'all', - selectedSort: 'date_added_desc', - showPermissionModal: false, - permissionType: null, - showPlaylistModal: false, - showChangeOwnerModal: false, - showPublishStateModal: false, - showCategoryModal: false, - showTagModal: false, - }; + this.state = { + channelMediaCount: -1, + author: ProfilePageStore.get('author-data'), + uploadsPreviewItemsCount: 0, + title: this.props.title, + query: ProfilePageStore.get('author-query'), + requestUrl: null, + selectedMedia: new Set(), + availableMediaIds: [], + showConfirmModal: false, + pendingAction: null, + confirmMessage: '', + listKey: 0, + notificationMessage: '', + showNotification: false, + notificationType: 'success', + hiddenFilters: true, + hiddenTags: true, + hiddenSorting: true, + filterArgs: '', + availableTags: [], + selectedTag: 'all', + selectedSort: 'date_added_desc', + showPermissionModal: false, + permissionType: null, + showPlaylistModal: false, + showChangeOwnerModal: false, + showPublishStateModal: false, + showCategoryModal: false, + showTagModal: false, + }; - this.authorDataLoad = this.authorDataLoad.bind(this); - this.onAuthorPreviewItemsCountCallback = this.onAuthorPreviewItemsCountCallback.bind(this); - this.getCountFunc = this.getCountFunc.bind(this); - this.changeRequestQuery = this.changeRequestQuery.bind(this); - this.handleMediaSelection = this.handleMediaSelection.bind(this); - this.handleBulkAction = this.handleBulkAction.bind(this); - this.handleConfirmCancel = this.handleConfirmCancel.bind(this); - this.handleConfirmProceed = this.handleConfirmProceed.bind(this); - this.clearSelectionAndRefresh = this.clearSelectionAndRefresh.bind(this); - this.clearSelection = this.clearSelection.bind(this); - this.executeEnableComments = this.executeEnableComments.bind(this); - this.executeDisableComments = this.executeDisableComments.bind(this); - this.executeEnableDownload = this.executeEnableDownload.bind(this); - this.executeDisableDownload = this.executeDisableDownload.bind(this); - this.executeCopyMedia = this.executeCopyMedia.bind(this); - this.showNotification = this.showNotification.bind(this); - this.handleSelectAll = this.handleSelectAll.bind(this); - this.handleDeselectAll = this.handleDeselectAll.bind(this); - this.handleItemsUpdate = this.handleItemsUpdate.bind(this); - this.onToggleFiltersClick = this.onToggleFiltersClick.bind(this); - this.onToggleTagsClick = this.onToggleTagsClick.bind(this); - this.onToggleSortingClick = this.onToggleSortingClick.bind(this); - this.onFiltersUpdate = this.onFiltersUpdate.bind(this); - this.onTagSelect = this.onTagSelect.bind(this); - this.onSortSelect = this.onSortSelect.bind(this); - this.onResponseDataLoaded = this.onResponseDataLoaded.bind(this); - this.handlePermissionModalCancel = this.handlePermissionModalCancel.bind(this); - this.handlePermissionModalSuccess = this.handlePermissionModalSuccess.bind(this); - this.handlePermissionModalError = this.handlePermissionModalError.bind(this); - this.handlePlaylistModalCancel = this.handlePlaylistModalCancel.bind(this); - this.handlePlaylistModalSuccess = this.handlePlaylistModalSuccess.bind(this); - this.handlePlaylistModalError = this.handlePlaylistModalError.bind(this); - this.handleChangeOwnerModalCancel = this.handleChangeOwnerModalCancel.bind(this); - this.handleChangeOwnerModalSuccess = this.handleChangeOwnerModalSuccess.bind(this); - this.handleChangeOwnerModalError = this.handleChangeOwnerModalError.bind(this); - this.handlePublishStateModalCancel = this.handlePublishStateModalCancel.bind(this); - this.handlePublishStateModalSuccess = this.handlePublishStateModalSuccess.bind(this); - this.handlePublishStateModalError = this.handlePublishStateModalError.bind(this); - this.handleCategoryModalCancel = this.handleCategoryModalCancel.bind(this); - this.handleCategoryModalSuccess = this.handleCategoryModalSuccess.bind(this); - this.handleCategoryModalError = this.handleCategoryModalError.bind(this); - this.handleTagModalCancel = this.handleTagModalCancel.bind(this); - this.handleTagModalSuccess = this.handleTagModalSuccess.bind(this); - this.handleTagModalError = this.handleTagModalError.bind(this); + this.authorDataLoad = this.authorDataLoad.bind(this); + this.onAuthorPreviewItemsCountCallback = this.onAuthorPreviewItemsCountCallback.bind(this); + this.getCountFunc = this.getCountFunc.bind(this); + this.changeRequestQuery = this.changeRequestQuery.bind(this); + this.handleMediaSelection = this.handleMediaSelection.bind(this); + this.handleBulkAction = this.handleBulkAction.bind(this); + this.handleConfirmCancel = this.handleConfirmCancel.bind(this); + this.handleConfirmProceed = this.handleConfirmProceed.bind(this); + this.clearSelectionAndRefresh = this.clearSelectionAndRefresh.bind(this); + this.clearSelection = this.clearSelection.bind(this); + this.executeEnableComments = this.executeEnableComments.bind(this); + this.executeDisableComments = this.executeDisableComments.bind(this); + this.executeEnableDownload = this.executeEnableDownload.bind(this); + this.executeDisableDownload = this.executeDisableDownload.bind(this); + this.executeCopyMedia = this.executeCopyMedia.bind(this); + this.showNotification = this.showNotification.bind(this); + this.handleSelectAll = this.handleSelectAll.bind(this); + this.handleDeselectAll = this.handleDeselectAll.bind(this); + this.handleItemsUpdate = this.handleItemsUpdate.bind(this); + this.onToggleFiltersClick = this.onToggleFiltersClick.bind(this); + this.onToggleTagsClick = this.onToggleTagsClick.bind(this); + this.onToggleSortingClick = this.onToggleSortingClick.bind(this); + this.onFiltersUpdate = this.onFiltersUpdate.bind(this); + this.onTagSelect = this.onTagSelect.bind(this); + this.onSortSelect = this.onSortSelect.bind(this); + this.onResponseDataLoaded = this.onResponseDataLoaded.bind(this); + this.handlePermissionModalCancel = this.handlePermissionModalCancel.bind(this); + this.handlePermissionModalSuccess = this.handlePermissionModalSuccess.bind(this); + this.handlePermissionModalError = this.handlePermissionModalError.bind(this); + this.handlePlaylistModalCancel = this.handlePlaylistModalCancel.bind(this); + this.handlePlaylistModalSuccess = this.handlePlaylistModalSuccess.bind(this); + this.handlePlaylistModalError = this.handlePlaylistModalError.bind(this); + this.handleChangeOwnerModalCancel = this.handleChangeOwnerModalCancel.bind(this); + this.handleChangeOwnerModalSuccess = this.handleChangeOwnerModalSuccess.bind(this); + this.handleChangeOwnerModalError = this.handleChangeOwnerModalError.bind(this); + this.handlePublishStateModalCancel = this.handlePublishStateModalCancel.bind(this); + this.handlePublishStateModalSuccess = this.handlePublishStateModalSuccess.bind(this); + this.handlePublishStateModalError = this.handlePublishStateModalError.bind(this); + this.handleCategoryModalCancel = this.handleCategoryModalCancel.bind(this); + this.handleCategoryModalSuccess = this.handleCategoryModalSuccess.bind(this); + this.handleCategoryModalError = this.handleCategoryModalError.bind(this); + this.handleTagModalCancel = this.handleTagModalCancel.bind(this); + this.handleTagModalSuccess = this.handleTagModalSuccess.bind(this); + this.handleTagModalError = this.handleTagModalError.bind(this); - ProfilePageStore.on('load-author-data', this.authorDataLoad); - } - - componentDidMount() { - ProfilePageActions.load_author_data(); - } - - authorDataLoad() { - const author = ProfilePageStore.get('author-data'); - - let requestUrl = this.state.requestUrl; - - if (author) { - if (this.state.query) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs; - } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + this.state.filterArgs; - } + ProfilePageStore.on('load-author-data', this.authorDataLoad); } - this.setState({ - author: author, - requestUrl: requestUrl, - }); - } + componentDidMount() { + ProfilePageActions.load_author_data(); + } - onAuthorPreviewItemsCountCallback(totalAuthorPreviewItems) { - this.setState({ - uploadsPreviewItemsCount: totalAuthorPreviewItems, - }); - } + authorDataLoad() { + const author = ProfilePageStore.get('author-data'); - getCountFunc(count) { - this.setState( - { - channelMediaCount: count, - }, - () => { - if (this.state.query) { - let title = ''; + let requestUrl = this.state.requestUrl; - if (!count) { - title = translateString('No results for') + ' "' + this.state.query + '"'; - } else if (1 === count) { - title = translateString('1 result for') + ' "' + this.state.query + '"'; - } else { - title = count + ' ' + translateString('results for') + ' "' + this.state.query + '"'; - } - - this.setState({ - title: title, - }); + if (author) { + if (this.state.query) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + author.id + + '&q=' + + encodeURIComponent(this.state.query) + + this.state.filterArgs; + } else { + requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + this.state.filterArgs; + } } - } - ); - } - changeRequestQuery(newQuery) { - if (!this.state.author) { - return; + this.setState({ + author: author, + requestUrl: requestUrl, + }); } - let requestUrl; - - if (newQuery) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&q=' + encodeURIComponent(newQuery) + this.state.filterArgs; - } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + this.state.filterArgs; + onAuthorPreviewItemsCountCallback(totalAuthorPreviewItems) { + this.setState({ + uploadsPreviewItemsCount: totalAuthorPreviewItems, + }); } - let title = this.state.title; + getCountFunc(count) { + this.setState( + { + channelMediaCount: count, + }, + () => { + if (this.state.query) { + let title = ''; - if ('' === newQuery) { - title = this.props.title; + if (!count) { + title = translateString('No results for') + ' "' + this.state.query + '"'; + } else if (1 === count) { + title = translateString('1 result for') + ' "' + this.state.query + '"'; + } else { + title = count + ' ' + translateString('results for') + ' "' + this.state.query + '"'; + } + + this.setState({ + title: title, + }); + } + } + ); } - this.setState({ - requestUrl: requestUrl, - query: newQuery, - title: title, - }); - } - - handleMediaSelection(mediaId, isSelected) { - this.setState((prevState) => { - const newSelectedMedia = new Set(prevState.selectedMedia); - if (isSelected) { - newSelectedMedia.add(mediaId); - } else { - newSelectedMedia.delete(mediaId); - } - return { selectedMedia: newSelectedMedia }; - }); - } - - handleBulkAction(action) { - const selectedCount = this.state.selectedMedia.size; - - if (selectedCount === 0) { - return; - } - - if (action === 'delete-media') { - this.setState({ - showConfirmModal: true, - pendingAction: action, - confirmMessage: translateString('You are going to delete') + ` ${selectedCount} ` + translateString('media, are you sure?'), - }); - } else if (action === 'enable-comments') { - this.setState({ - showConfirmModal: true, - pendingAction: action, - confirmMessage: translateString('You are going to enable comments to') + ` ${selectedCount} ` + translateString('media, are you sure?'), - }); - } else if (action === 'disable-comments') { - this.setState({ - showConfirmModal: true, - pendingAction: action, - confirmMessage: translateString('You are going to disable comments to') + ` ${selectedCount} ` + translateString('media, are you sure?'), - }); - } else if (action === 'enable-download') { - this.setState({ - showConfirmModal: true, - pendingAction: action, - confirmMessage: translateString('You are going to enable download for') + ` ${selectedCount} ` + translateString('media, are you sure?'), - }); - } else if (action === 'disable-download') { - this.setState({ - showConfirmModal: true, - pendingAction: action, - confirmMessage: translateString('You are going to disable download for') + ` ${selectedCount} ` + translateString('media, are you sure?'), - }); - } else if (action === 'copy-media') { - this.setState({ - showConfirmModal: true, - pendingAction: action, - confirmMessage: translateString('You are going to copy') + ` ${selectedCount} ` + translateString('media, are you sure?'), - }); - } else if (action === 'add-remove-coviewers') { - this.setState({ - showPermissionModal: true, - permissionType: 'viewer', - }); - } else if (action === 'add-remove-coeditors') { - this.setState({ - showPermissionModal: true, - permissionType: 'editor', - }); - } else if (action === 'add-remove-coowners') { - this.setState({ - showPermissionModal: true, - permissionType: 'owner', - }); - } else if (action === 'add-remove-playlist') { - this.setState({ - showPlaylistModal: true, - }); - } else if (action === 'change-owner') { - this.setState({ - showChangeOwnerModal: true, - }); - } else if (action === 'publish-state') { - this.setState({ - showPublishStateModal: true, - }); - } else if (action === 'add-remove-category') { - this.setState({ - showCategoryModal: true, - }); - } else if (action === 'add-remove-tags') { - this.setState({ - showTagModal: true, - }); - } else { - // Other actions can be implemented later - } - } - - handleConfirmCancel() { - this.setState({ - showConfirmModal: false, - pendingAction: null, - confirmMessage: '', - }); - } - - handleConfirmProceed() { - const action = this.state.pendingAction; - this.setState({ - showConfirmModal: false, - pendingAction: null, - confirmMessage: '', - }); - - if (action === 'delete-media') { - this.executeDeleteMedia(); - } else if (action === 'enable-comments') { - this.executeEnableComments(); - } else if (action === 'disable-comments') { - this.executeDisableComments(); - } else if (action === 'enable-download') { - this.executeEnableDownload(); - } else if (action === 'disable-download') { - this.executeDisableDownload(); - } else if (action === 'copy-media') { - this.executeCopyMedia(); - } - } - - executeDeleteMedia() { - const selectedIds = Array.from(this.state.selectedMedia); - const selectedCount = selectedIds.length; - - fetch('/api/v1/media/user/bulk_actions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': this.getCsrfToken(), - }, - body: JSON.stringify({ - action: 'delete_media', - media_ids: selectedIds, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to delete media'); - } - return response.json(); - }) - .then((data) => { - const message = selectedCount === 1 - ? translateString('The media was deleted successfully.') - : translateString('Successfully deleted') + ` ${selectedCount} ` + translateString('media.'); - this.showNotification(message); - this.clearSelectionAndRefresh(); - }) - .catch((error) => { - this.showNotification(translateString('Failed to delete media. Please try again.'), 'error'); - this.clearSelectionAndRefresh(); - }); - } - - getCsrfToken() { - const name = 'csrftoken'; - let cookieValue = null; - if (document.cookie && document.cookie !== '') { - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - if (cookie.substring(0, name.length + 1) === name + '=') { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - clearSelectionAndRefresh() { - // Clear selected media and increment listKey to force re-render - this.setState((prevState) => ({ - selectedMedia: new Set(), - listKey: prevState.listKey + 1, - })); - } - - clearSelection() { - // Clear selected media without refreshing - this.setState({ - selectedMedia: new Set(), - }); - } - - executeEnableComments() { - const selectedIds = Array.from(this.state.selectedMedia); - - fetch('/api/v1/media/user/bulk_actions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': this.getCsrfToken(), - }, - body: JSON.stringify({ - action: 'enable_comments', - media_ids: selectedIds, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to enable comments'); - } - return response.json(); - }) - .then((data) => { - this.showNotification(translateString('Successfully Enabled comments')); - this.clearSelection(); - }) - .catch((error) => { - this.showNotification(translateString('Failed to enable comments.'), 'error'); - this.clearSelection(); - }); - } - - executeDisableComments() { - const selectedIds = Array.from(this.state.selectedMedia); - - fetch('/api/v1/media/user/bulk_actions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': this.getCsrfToken(), - }, - body: JSON.stringify({ - action: 'disable_comments', - media_ids: selectedIds, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to disable comments'); - } - return response.json(); - }) - .then((data) => { - this.showNotification(translateString('Successfully Disabled comments')); - this.clearSelection(); - }) - .catch((error) => { - this.showNotification(translateString('Failed to disable comments.'), 'error'); - this.clearSelection(); - }); - } - - executeEnableDownload() { - const selectedIds = Array.from(this.state.selectedMedia); - - fetch('/api/v1/media/user/bulk_actions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': this.getCsrfToken(), - }, - body: JSON.stringify({ - action: 'enable_download', - media_ids: selectedIds, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to enable download'); - } - return response.json(); - }) - .then((data) => { - this.showNotification(translateString('Successfully Enabled Download')); - this.clearSelection(); - }) - .catch((error) => { - this.showNotification(translateString('Failed to enable download.'), 'error'); - this.clearSelection(); - }); - } - - executeDisableDownload() { - const selectedIds = Array.from(this.state.selectedMedia); - - fetch('/api/v1/media/user/bulk_actions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': this.getCsrfToken(), - }, - body: JSON.stringify({ - action: 'disable_download', - media_ids: selectedIds, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to disable download'); - } - return response.json(); - }) - .then((data) => { - this.showNotification(translateString('Successfully Disabled Download')); - this.clearSelection(); - }) - .catch((error) => { - this.showNotification(translateString('Failed to disable download.'), 'error'); - this.clearSelection(); - }); - } - - executeCopyMedia() { - const selectedIds = Array.from(this.state.selectedMedia); - - fetch('/api/v1/media/user/bulk_actions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': this.getCsrfToken(), - }, - body: JSON.stringify({ - action: 'copy_media', - media_ids: selectedIds, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to copy media'); - } - return response.json(); - }) - .then((data) => { - this.showNotification(translateString('Successfully Copied')); - this.clearSelectionAndRefresh(); - }) - .catch((error) => { - this.showNotification(translateString('Failed to copy media.'), 'error'); - this.clearSelection(); - }); - } - - showNotification(message, type = 'success') { - this.setState({ - notificationMessage: message, - showNotification: true, - notificationType: type, - }); - - setTimeout(() => { - this.setState({ showNotification: false }); - }, 5000); - } - - handleItemsUpdate(items) { - // Extract media IDs from loaded items - const mediaIds = items.map((item) => item.friendly_token || item.uid || item.id); - this.setState({ availableMediaIds: mediaIds }); - } - - handleSelectAll() { - // Select all available media - this.setState({ - selectedMedia: new Set(this.state.availableMediaIds), - }); - } - - handleDeselectAll() { - // Clear all selections - this.setState({ - selectedMedia: new Set(), - }); - } - - onToggleFiltersClick() { - this.setState({ - hiddenFilters: !this.state.hiddenFilters, - hiddenTags: true, - hiddenSorting: true, - }); - } - - onToggleTagsClick() { - this.setState({ - hiddenFilters: true, - hiddenTags: !this.state.hiddenTags, - hiddenSorting: true, - }); - } - - onToggleSortingClick() { - this.setState({ - hiddenFilters: true, - hiddenTags: true, - hiddenSorting: !this.state.hiddenSorting, - }); - } - - onTagSelect(tag) { - this.setState({ selectedTag: tag }, () => { - // Apply tag filter - this.onFiltersUpdate({ - media_type: this.state.filterArgs.includes('media_type') ? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1] : null, - upload_date: this.state.filterArgs.includes('upload_date') ? this.state.filterArgs.match(/upload_date=([^&]*)/)?.[1] : null, - duration: this.state.filterArgs.includes('duration') ? this.state.filterArgs.match(/duration=([^&]*)/)?.[1] : null, - publish_state: this.state.filterArgs.includes('publish_state') ? this.state.filterArgs.match(/publish_state=([^&]*)/)?.[1] : null, - sort_by: this.state.selectedSort, - tag: tag, - }); - }); - } - - onSortSelect(sortOption) { - this.setState({ selectedSort: sortOption }, () => { - // Apply sort filter - this.onFiltersUpdate({ - media_type: this.state.filterArgs.includes('media_type') ? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1] : null, - upload_date: this.state.filterArgs.includes('upload_date') ? this.state.filterArgs.match(/upload_date=([^&]*)/)?.[1] : null, - duration: this.state.filterArgs.includes('duration') ? this.state.filterArgs.match(/duration=([^&]*)/)?.[1] : null, - publish_state: this.state.filterArgs.includes('publish_state') ? this.state.filterArgs.match(/publish_state=([^&]*)/)?.[1] : null, - sort_by: sortOption, - tag: this.state.selectedTag, - }); - }); - } - - onFiltersUpdate(updatedArgs) { - const args = { - media_type: null, - upload_date: null, - duration: null, - publish_state: null, - sort_by: null, - ordering: null, - t: null, - }; - - switch (updatedArgs.media_type) { - case 'video': - case 'audio': - case 'image': - case 'pdf': - args.media_type = updatedArgs.media_type; - break; - } - - switch (updatedArgs.upload_date) { - case 'today': - case 'this_week': - case 'this_month': - case 'this_year': - args.upload_date = updatedArgs.upload_date; - break; - } - - // Handle duration filter - if (updatedArgs.duration && updatedArgs.duration !== 'all') { - args.duration = updatedArgs.duration; - } - - // Handle publish state filter - if (updatedArgs.publish_state && updatedArgs.publish_state !== 'all') { - args.publish_state = updatedArgs.publish_state; - } - - switch (updatedArgs.sort_by) { - case 'date_added_desc': - // Default sorting, no need to add parameters - break; - case 'date_added_asc': - args.ordering = 'asc'; - break; - case 'alphabetically_asc': - args.sort_by = 'title_asc'; - break; - case 'alphabetically_desc': - args.sort_by = 'title_desc'; - break; - case 'plays_least': - args.sort_by = 'views_asc'; - break; - case 'plays_most': - args.sort_by = 'views_desc'; - break; - case 'likes_least': - args.sort_by = 'likes_asc'; - break; - case 'likes_most': - args.sort_by = 'likes_desc'; - break; - } - - // Handle tag filter - if (updatedArgs.tag && updatedArgs.tag !== 'all') { - args.t = updatedArgs.tag; - } - - const newArgs = []; - - for (let arg in args) { - if (null !== args[arg]) { - newArgs.push(arg + '=' + args[arg]); - } - } - - this.setState( - { - filterArgs: newArgs.length ? '&' + newArgs.join('&') : '', - selectedMedia: new Set(), // Clear selected items when filter changes - }, - function () { - // Update the request URL with new filter args + changeRequestQuery(newQuery) { if (!this.state.author) { - return; + return; } let requestUrl; - if (this.state.query) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs; + if (newQuery) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&q=' + + encodeURIComponent(newQuery) + + this.state.filterArgs; } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + this.state.filterArgs; + requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + this.state.filterArgs; + } + + let title = this.state.title; + + if ('' === newQuery) { + title = this.props.title; } this.setState({ - requestUrl: requestUrl, + requestUrl: requestUrl, + query: newQuery, + title: title, }); - } - ); - } - - handlePermissionModalCancel() { - this.setState({ - showPermissionModal: false, - permissionType: null, - }); - } - - handlePermissionModalSuccess(message) { - this.showNotification(message); - this.clearSelection(); - this.setState({ - showPermissionModal: false, - permissionType: null, - }); - } - - handlePermissionModalError(message) { - this.showNotification(message, 'error'); - this.setState({ - showPermissionModal: false, - permissionType: null, - }); - } - - handlePlaylistModalCancel() { - this.setState({ - showPlaylistModal: false, - }); - } - - handlePlaylistModalSuccess(message) { - this.showNotification(message); - this.clearSelection(); - this.setState({ - showPlaylistModal: false, - }); - } - - handlePlaylistModalError(message) { - this.showNotification(message, 'error'); - this.setState({ - showPlaylistModal: false, - }); - } - - handleChangeOwnerModalCancel() { - this.setState({ - showChangeOwnerModal: false, - }); - } - - handleChangeOwnerModalSuccess(message) { - this.showNotification(message); - this.clearSelectionAndRefresh(); - this.setState({ - showChangeOwnerModal: false, - }); - } - - handleChangeOwnerModalError(message) { - this.showNotification(message, 'error'); - this.setState({ - showChangeOwnerModal: false, - }); - } - - handlePublishStateModalCancel() { - this.setState({ - showPublishStateModal: false, - }); - } - - handlePublishStateModalSuccess(message) { - this.showNotification(message); - this.clearSelectionAndRefresh(); - this.setState({ - showPublishStateModal: false, - }); - } - - handlePublishStateModalError(message) { - this.showNotification(message, 'error'); - this.setState({ - showPublishStateModal: false, - }); - } - - handleCategoryModalCancel() { - this.setState({ - showCategoryModal: false, - }); - } - - handleCategoryModalSuccess(message) { - this.showNotification(message); - this.clearSelection(); - this.setState({ - showCategoryModal: false, - }); - } - - handleCategoryModalError(message) { - this.showNotification(message, 'error'); - this.setState({ - showCategoryModal: false, - }); - } - - handleTagModalCancel() { - this.setState({ - showTagModal: false, - }); - } - - handleTagModalSuccess(message) { - this.showNotification(message); - this.clearSelection(); - this.setState({ - showTagModal: false, - }); - } - - handleTagModalError(message) { - this.showNotification(message, 'error'); - this.setState({ - showTagModal: false, - }); - } - - onResponseDataLoaded(responseData) { - // Extract tags from response - if (responseData && responseData.tags) { - const tags = responseData.tags.split(',').map((tag) => tag.trim()).filter((tag) => tag); - this.setState({ availableTags: tags }); } - } - pageContent() { - const authorData = ProfilePageStore.get('author-data'); + handleMediaSelection(mediaId, isSelected) { + this.setState((prevState) => { + const newSelectedMedia = new Set(prevState.selectedMedia); + if (isSelected) { + newSelectedMedia.add(mediaId); + } else { + newSelectedMedia.delete(mediaId); + } + return { selectedMedia: newSelectedMedia }; + }); + } - const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username; + handleBulkAction(action) { + const selectedCount = this.state.selectedMedia.size; - // Check if any filters are active (excluding default sort and tags) - const hasActiveFilters = this.state.filterArgs && ( - this.state.filterArgs.includes('media_type=') || - this.state.filterArgs.includes('upload_date=') || - this.state.filterArgs.includes('duration=') || - this.state.filterArgs.includes('publish_state=') - ); + if (selectedCount === 0) { + return; + } - const hasActiveTags = this.state.selectedTag && this.state.selectedTag !== 'all'; - const hasActiveSort = this.state.selectedSort && this.state.selectedSort !== 'date_added_desc'; + if (action === 'delete-media') { + this.setState({ + showConfirmModal: true, + pendingAction: action, + confirmMessage: + translateString('You are going to delete') + + ` ${selectedCount} ` + + translateString('media, are you sure?'), + }); + } else if (action === 'enable-comments') { + this.setState({ + showConfirmModal: true, + pendingAction: action, + confirmMessage: + translateString('You are going to enable comments to') + + ` ${selectedCount} ` + + translateString('media, are you sure?'), + }); + } else if (action === 'disable-comments') { + this.setState({ + showConfirmModal: true, + pendingAction: action, + confirmMessage: + translateString('You are going to disable comments to') + + ` ${selectedCount} ` + + translateString('media, are you sure?'), + }); + } else if (action === 'enable-download') { + this.setState({ + showConfirmModal: true, + pendingAction: action, + confirmMessage: + translateString('You are going to enable download for') + + ` ${selectedCount} ` + + translateString('media, are you sure?'), + }); + } else if (action === 'disable-download') { + this.setState({ + showConfirmModal: true, + pendingAction: action, + confirmMessage: + translateString('You are going to disable download for') + + ` ${selectedCount} ` + + translateString('media, are you sure?'), + }); + } else if (action === 'copy-media') { + this.setState({ + showConfirmModal: true, + pendingAction: action, + confirmMessage: + translateString('You are going to copy') + + ` ${selectedCount} ` + + translateString('media, are you sure?'), + }); + } else if (action === 'add-remove-coviewers') { + this.setState({ + showPermissionModal: true, + permissionType: 'viewer', + }); + } else if (action === 'add-remove-coeditors') { + this.setState({ + showPermissionModal: true, + permissionType: 'editor', + }); + } else if (action === 'add-remove-coowners') { + this.setState({ + showPermissionModal: true, + permissionType: 'owner', + }); + } else if (action === 'add-remove-playlist') { + this.setState({ + showPlaylistModal: true, + }); + } else if (action === 'change-owner') { + this.setState({ + showChangeOwnerModal: true, + }); + } else if (action === 'publish-state') { + this.setState({ + showPublishStateModal: true, + }); + } else if (action === 'add-remove-category') { + this.setState({ + showCategoryModal: true, + }); + } else if (action === 'add-remove-tags') { + this.setState({ + showTagModal: true, + }); + } else { + // Other actions can be implemented later + } + } - return [ - this.state.author ? ( - - ) : null, - this.state.author ? ( - - - - - ) : null, - , - , - , - , - , - , - , - this.state.showNotification ? ( -
    - {this.state.notificationMessage} -
    - ) : null, - ]; - } + handleConfirmCancel() { + this.setState({ + showConfirmModal: false, + pendingAction: null, + confirmMessage: '', + }); + } + + handleConfirmProceed() { + const action = this.state.pendingAction; + this.setState({ + showConfirmModal: false, + pendingAction: null, + confirmMessage: '', + }); + + if (action === 'delete-media') { + this.executeDeleteMedia(); + } else if (action === 'enable-comments') { + this.executeEnableComments(); + } else if (action === 'disable-comments') { + this.executeDisableComments(); + } else if (action === 'enable-download') { + this.executeEnableDownload(); + } else if (action === 'disable-download') { + this.executeDisableDownload(); + } else if (action === 'copy-media') { + this.executeCopyMedia(); + } + } + + executeDeleteMedia() { + const selectedIds = Array.from(this.state.selectedMedia); + const selectedCount = selectedIds.length; + + fetch('/api/v1/media/user/bulk_actions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': this.getCsrfToken(), + }, + body: JSON.stringify({ + action: 'delete_media', + media_ids: selectedIds, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to delete media'); + } + return response.json(); + }) + .then((data) => { + const message = + selectedCount === 1 + ? translateString('The media was deleted successfully.') + : translateString('Successfully deleted') + ` ${selectedCount} ` + translateString('media.'); + this.showNotification(message); + this.clearSelectionAndRefresh(); + }) + .catch((error) => { + this.showNotification(translateString('Failed to delete media. Please try again.'), 'error'); + this.clearSelectionAndRefresh(); + }); + } + + getCsrfToken() { + const name = 'csrftoken'; + let cookieValue = null; + if (document.cookie && document.cookie !== '') { + const cookies = document.cookie.split(';'); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + if (cookie.substring(0, name.length + 1) === name + '=') { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + clearSelectionAndRefresh() { + // Clear selected media and increment listKey to force re-render + this.setState((prevState) => ({ + selectedMedia: new Set(), + listKey: prevState.listKey + 1, + })); + } + + clearSelection() { + // Clear selected media without refreshing + this.setState({ + selectedMedia: new Set(), + }); + } + + executeEnableComments() { + const selectedIds = Array.from(this.state.selectedMedia); + + fetch('/api/v1/media/user/bulk_actions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': this.getCsrfToken(), + }, + body: JSON.stringify({ + action: 'enable_comments', + media_ids: selectedIds, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to enable comments'); + } + return response.json(); + }) + .then((data) => { + this.showNotification(translateString('Successfully Enabled comments')); + this.clearSelection(); + }) + .catch((error) => { + this.showNotification(translateString('Failed to enable comments.'), 'error'); + this.clearSelection(); + }); + } + + executeDisableComments() { + const selectedIds = Array.from(this.state.selectedMedia); + + fetch('/api/v1/media/user/bulk_actions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': this.getCsrfToken(), + }, + body: JSON.stringify({ + action: 'disable_comments', + media_ids: selectedIds, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to disable comments'); + } + return response.json(); + }) + .then((data) => { + this.showNotification(translateString('Successfully Disabled comments')); + this.clearSelection(); + }) + .catch((error) => { + this.showNotification(translateString('Failed to disable comments.'), 'error'); + this.clearSelection(); + }); + } + + executeEnableDownload() { + const selectedIds = Array.from(this.state.selectedMedia); + + fetch('/api/v1/media/user/bulk_actions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': this.getCsrfToken(), + }, + body: JSON.stringify({ + action: 'enable_download', + media_ids: selectedIds, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to enable download'); + } + return response.json(); + }) + .then((data) => { + this.showNotification(translateString('Successfully Enabled Download')); + this.clearSelection(); + }) + .catch((error) => { + this.showNotification(translateString('Failed to enable download.'), 'error'); + this.clearSelection(); + }); + } + + executeDisableDownload() { + const selectedIds = Array.from(this.state.selectedMedia); + + fetch('/api/v1/media/user/bulk_actions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': this.getCsrfToken(), + }, + body: JSON.stringify({ + action: 'disable_download', + media_ids: selectedIds, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to disable download'); + } + return response.json(); + }) + .then((data) => { + this.showNotification(translateString('Successfully Disabled Download')); + this.clearSelection(); + }) + .catch((error) => { + this.showNotification(translateString('Failed to disable download.'), 'error'); + this.clearSelection(); + }); + } + + executeCopyMedia() { + const selectedIds = Array.from(this.state.selectedMedia); + + fetch('/api/v1/media/user/bulk_actions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': this.getCsrfToken(), + }, + body: JSON.stringify({ + action: 'copy_media', + media_ids: selectedIds, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to copy media'); + } + return response.json(); + }) + .then((data) => { + this.showNotification(translateString('Successfully Copied')); + this.clearSelectionAndRefresh(); + }) + .catch((error) => { + this.showNotification(translateString('Failed to copy media.'), 'error'); + this.clearSelection(); + }); + } + + showNotification(message, type = 'success') { + this.setState({ + notificationMessage: message, + showNotification: true, + notificationType: type, + }); + + setTimeout(() => { + this.setState({ showNotification: false }); + }, 5000); + } + + handleItemsUpdate(items) { + // Extract media IDs from loaded items + const mediaIds = items.map((item) => item.friendly_token || item.uid || item.id); + this.setState({ availableMediaIds: mediaIds }); + } + + handleSelectAll() { + // Select all available media + this.setState({ + selectedMedia: new Set(this.state.availableMediaIds), + }); + } + + handleDeselectAll() { + // Clear all selections + this.setState({ + selectedMedia: new Set(), + }); + } + + onToggleFiltersClick() { + this.setState({ + hiddenFilters: !this.state.hiddenFilters, + hiddenTags: true, + hiddenSorting: true, + }); + } + + onToggleTagsClick() { + this.setState({ + hiddenFilters: true, + hiddenTags: !this.state.hiddenTags, + hiddenSorting: true, + }); + } + + onToggleSortingClick() { + this.setState({ + hiddenFilters: true, + hiddenTags: true, + hiddenSorting: !this.state.hiddenSorting, + }); + } + + onTagSelect(tag) { + this.setState({ selectedTag: tag }, () => { + // Apply tag filter + this.onFiltersUpdate({ + media_type: this.state.filterArgs.includes('media_type') + ? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1] + : null, + upload_date: this.state.filterArgs.includes('upload_date') + ? this.state.filterArgs.match(/upload_date=([^&]*)/)?.[1] + : null, + duration: this.state.filterArgs.includes('duration') + ? this.state.filterArgs.match(/duration=([^&]*)/)?.[1] + : null, + publish_state: this.state.filterArgs.includes('publish_state') + ? this.state.filterArgs.match(/publish_state=([^&]*)/)?.[1] + : null, + sort_by: this.state.selectedSort, + tag: tag, + }); + }); + } + + onSortSelect(sortOption) { + this.setState({ selectedSort: sortOption }, () => { + // Apply sort filter + this.onFiltersUpdate({ + media_type: this.state.filterArgs.includes('media_type') + ? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1] + : null, + upload_date: this.state.filterArgs.includes('upload_date') + ? this.state.filterArgs.match(/upload_date=([^&]*)/)?.[1] + : null, + duration: this.state.filterArgs.includes('duration') + ? this.state.filterArgs.match(/duration=([^&]*)/)?.[1] + : null, + publish_state: this.state.filterArgs.includes('publish_state') + ? this.state.filterArgs.match(/publish_state=([^&]*)/)?.[1] + : null, + sort_by: sortOption, + tag: this.state.selectedTag, + }); + }); + } + + onFiltersUpdate(updatedArgs) { + const args = { + media_type: null, + upload_date: null, + duration: null, + publish_state: null, + sort_by: null, + ordering: null, + t: null, + }; + + switch (updatedArgs.media_type) { + case 'video': + case 'audio': + case 'image': + case 'pdf': + args.media_type = updatedArgs.media_type; + break; + } + + switch (updatedArgs.upload_date) { + case 'today': + case 'this_week': + case 'this_month': + case 'this_year': + args.upload_date = updatedArgs.upload_date; + break; + } + + // Handle duration filter + if (updatedArgs.duration && updatedArgs.duration !== 'all') { + args.duration = updatedArgs.duration; + } + + // Handle publish state filter + if (updatedArgs.publish_state && updatedArgs.publish_state !== 'all') { + args.publish_state = updatedArgs.publish_state; + } + + switch (updatedArgs.sort_by) { + case 'date_added_desc': + // Default sorting, no need to add parameters + break; + case 'date_added_asc': + args.ordering = 'asc'; + break; + case 'alphabetically_asc': + args.sort_by = 'title_asc'; + break; + case 'alphabetically_desc': + args.sort_by = 'title_desc'; + break; + case 'plays_least': + args.sort_by = 'views_asc'; + break; + case 'plays_most': + args.sort_by = 'views_desc'; + break; + case 'likes_least': + args.sort_by = 'likes_asc'; + break; + case 'likes_most': + args.sort_by = 'likes_desc'; + break; + } + + // Handle tag filter + if (updatedArgs.tag && updatedArgs.tag !== 'all') { + args.t = updatedArgs.tag; + } + + const newArgs = []; + + for (let arg in args) { + if (null !== args[arg]) { + newArgs.push(arg + '=' + args[arg]); + } + } + + this.setState( + { + filterArgs: newArgs.length ? '&' + newArgs.join('&') : '', + selectedMedia: new Set(), // Clear selected items when filter changes + }, + function () { + // Update the request URL with new filter args + if (!this.state.author) { + return; + } + + let requestUrl; + + if (this.state.query) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&q=' + + encodeURIComponent(this.state.query) + + this.state.filterArgs; + } else { + requestUrl = + ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + this.state.filterArgs; + } + + this.setState({ + requestUrl: requestUrl, + }); + } + ); + } + + handlePermissionModalCancel() { + this.setState({ + showPermissionModal: false, + permissionType: null, + }); + } + + handlePermissionModalSuccess(message) { + this.showNotification(message); + this.clearSelection(); + this.setState({ + showPermissionModal: false, + permissionType: null, + }); + } + + handlePermissionModalError(message) { + this.showNotification(message, 'error'); + this.setState({ + showPermissionModal: false, + permissionType: null, + }); + } + + handlePlaylistModalCancel() { + this.setState({ + showPlaylistModal: false, + }); + } + + handlePlaylistModalSuccess(message) { + this.showNotification(message); + this.clearSelection(); + this.setState({ + showPlaylistModal: false, + }); + } + + handlePlaylistModalError(message) { + this.showNotification(message, 'error'); + this.setState({ + showPlaylistModal: false, + }); + } + + handleChangeOwnerModalCancel() { + this.setState({ + showChangeOwnerModal: false, + }); + } + + handleChangeOwnerModalSuccess(message) { + this.showNotification(message); + this.clearSelectionAndRefresh(); + this.setState({ + showChangeOwnerModal: false, + }); + } + + handleChangeOwnerModalError(message) { + this.showNotification(message, 'error'); + this.setState({ + showChangeOwnerModal: false, + }); + } + + handlePublishStateModalCancel() { + this.setState({ + showPublishStateModal: false, + }); + } + + handlePublishStateModalSuccess(message) { + this.showNotification(message); + this.clearSelectionAndRefresh(); + this.setState({ + showPublishStateModal: false, + }); + } + + handlePublishStateModalError(message) { + this.showNotification(message, 'error'); + this.setState({ + showPublishStateModal: false, + }); + } + + handleCategoryModalCancel() { + this.setState({ + showCategoryModal: false, + }); + } + + handleCategoryModalSuccess(message) { + this.showNotification(message); + this.clearSelection(); + this.setState({ + showCategoryModal: false, + }); + } + + handleCategoryModalError(message) { + this.showNotification(message, 'error'); + this.setState({ + showCategoryModal: false, + }); + } + + handleTagModalCancel() { + this.setState({ + showTagModal: false, + }); + } + + handleTagModalSuccess(message) { + this.showNotification(message); + this.clearSelection(); + this.setState({ + showTagModal: false, + }); + } + + handleTagModalError(message) { + this.showNotification(message, 'error'); + this.setState({ + showTagModal: false, + }); + } + + onResponseDataLoaded(responseData) { + // Extract tags from response + if (responseData && responseData.tags) { + const tags = responseData.tags + .split(',') + .map((tag) => tag.trim()) + .filter((tag) => tag); + this.setState({ availableTags: tags }); + } + } + + pageContent() { + const authorData = ProfilePageStore.get('author-data'); + + const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username; + + // Check if any filters are active (excluding default sort and tags) + const hasActiveFilters = + this.state.filterArgs && + (this.state.filterArgs.includes('media_type=') || + this.state.filterArgs.includes('upload_date=') || + this.state.filterArgs.includes('duration=') || + this.state.filterArgs.includes('publish_state=')); + + const hasActiveTags = this.state.selectedTag && this.state.selectedTag !== 'all'; + const hasActiveSort = this.state.selectedSort && this.state.selectedSort !== 'date_added_desc'; + + return [ + this.state.author ? ( + + ) : null, + this.state.author ? ( + + + + + ) : null, + , + , + , + , + , + , + , + this.state.showNotification ? ( +
    + {this.state.notificationMessage} +
    + ) : null, + ]; + } } ProfileMediaPage.propTypes = { - title: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, }; ProfileMediaPage.defaultProps = { - title: 'Uploads', + title: 'Uploads', }; diff --git a/frontend/src/static/js/pages/ProfileSharedByMePage.js b/frontend/src/static/js/pages/ProfileSharedByMePage.js index a026e9b3..a0f9a14f 100644 --- a/frontend/src/static/js/pages/ProfileSharedByMePage.js +++ b/frontend/src/static/js/pages/ProfileSharedByMePage.js @@ -11,7 +11,7 @@ import { ProfileMediaFilters } from '../components/search-filters/ProfileMediaFi import { ProfileMediaTags } from '../components/search-filters/ProfileMediaTags'; import { ProfileMediaSorting } from '../components/search-filters/ProfileMediaSorting'; import { BulkActionsModals } from '../components/BulkActionsModals'; -import { translateString } from '../utils/helpers'; +import { inEmbeddedApp, translateString } from '../utils/helpers'; import { withBulkActions } from '../utils/hoc/withBulkActions'; import { Page } from './_Page'; @@ -19,400 +19,443 @@ import { Page } from './_Page'; import '../components/profile-page/ProfilePage.scss'; function EmptySharedByMe(props) { - return ( - - {(links) => ( -
    -
    No shared media
    -
    - Media that you have shared with others will show up here. -
    -
    - )} -
    - ); + return ( + + {(links) => ( +
    +
    No shared media
    +
    Media that you have shared with others will show up here.
    +
    + )} +
    + ); } class ProfileSharedByMePage extends Page { - constructor(props, pageSlug) { - super(props, 'string' === typeof pageSlug ? pageSlug : 'author-shared-by-me'); + constructor(props, pageSlug) { + super(props, 'string' === typeof pageSlug ? pageSlug : 'author-shared-by-me'); - this.profilePageSlug = 'string' === typeof pageSlug ? pageSlug : 'author-shared-by-me'; + this.profilePageSlug = 'string' === typeof pageSlug ? pageSlug : 'author-shared-by-me'; - this.state = { - channelMediaCount: -1, - author: ProfilePageStore.get('author-data'), - uploadsPreviewItemsCount: 0, - title: this.props.title, - query: ProfilePageStore.get('author-query'), - requestUrl: null, - hiddenFilters: true, - hiddenTags: true, - hiddenSorting: true, - filterArgs: '', - availableTags: [], - selectedTag: 'all', - selectedSort: 'date_added_desc', - }; + this.state = { + channelMediaCount: -1, + author: ProfilePageStore.get('author-data'), + uploadsPreviewItemsCount: 0, + title: this.props.title, + query: ProfilePageStore.get('author-query'), + requestUrl: null, + hiddenFilters: true, + hiddenTags: true, + hiddenSorting: true, + filterArgs: '', + availableTags: [], + selectedTag: 'all', + selectedSort: 'date_added_desc', + }; - this.authorDataLoad = this.authorDataLoad.bind(this); - this.onAuthorPreviewItemsCountCallback = this.onAuthorPreviewItemsCountCallback.bind(this); - this.getCountFunc = this.getCountFunc.bind(this); - this.changeRequestQuery = this.changeRequestQuery.bind(this); - this.onToggleFiltersClick = this.onToggleFiltersClick.bind(this); - this.onToggleTagsClick = this.onToggleTagsClick.bind(this); - this.onToggleSortingClick = this.onToggleSortingClick.bind(this); - this.onFiltersUpdate = this.onFiltersUpdate.bind(this); - this.onTagSelect = this.onTagSelect.bind(this); - this.onSortSelect = this.onSortSelect.bind(this); - this.onResponseDataLoaded = this.onResponseDataLoaded.bind(this); + this.authorDataLoad = this.authorDataLoad.bind(this); + this.onAuthorPreviewItemsCountCallback = this.onAuthorPreviewItemsCountCallback.bind(this); + this.getCountFunc = this.getCountFunc.bind(this); + this.changeRequestQuery = this.changeRequestQuery.bind(this); + this.onToggleFiltersClick = this.onToggleFiltersClick.bind(this); + this.onToggleTagsClick = this.onToggleTagsClick.bind(this); + this.onToggleSortingClick = this.onToggleSortingClick.bind(this); + this.onFiltersUpdate = this.onFiltersUpdate.bind(this); + this.onTagSelect = this.onTagSelect.bind(this); + this.onSortSelect = this.onSortSelect.bind(this); + this.onResponseDataLoaded = this.onResponseDataLoaded.bind(this); - ProfilePageStore.on('load-author-data', this.authorDataLoad); - } - - componentDidMount() { - ProfilePageActions.load_author_data(); - } - - authorDataLoad() { - const author = ProfilePageStore.get('author-data'); - - let requestUrl = this.state.requestUrl; - - if (author) { - if (this.state.query) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_by_me&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs; - } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_by_me' + this.state.filterArgs; - } + ProfilePageStore.on('load-author-data', this.authorDataLoad); } - this.setState({ - author: author, - requestUrl: requestUrl, - }); - } + componentDidMount() { + ProfilePageActions.load_author_data(); + } - onAuthorPreviewItemsCountCallback(totalAuthorPreviewItems) { - this.setState({ - uploadsPreviewItemsCount: totalAuthorPreviewItems, - }); - } + authorDataLoad() { + const author = ProfilePageStore.get('author-data'); - getCountFunc(count) { - this.setState( - { - channelMediaCount: count, - }, - () => { - if (this.state.query) { - let title = ''; + let requestUrl = this.state.requestUrl; - if (!count) { - title = 'No results for "' + this.state.query + '"'; - } else if (1 === count) { - title = '1 result for "' + this.state.query + '"'; - } else { - title = count + ' results for "' + this.state.query + '"'; - } - - this.setState({ - title: title, - }); + if (author) { + if (this.state.query) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + author.id + + '&show=shared_by_me&q=' + + encodeURIComponent(this.state.query) + + this.state.filterArgs; + } else { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + author.id + + '&show=shared_by_me' + + this.state.filterArgs; + } } - } - ); - } - changeRequestQuery(newQuery) { - if (!this.state.author) { - return; + this.setState({ + author: author, + requestUrl: requestUrl, + }); } - let requestUrl; - - if (newQuery) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_by_me&q=' + encodeURIComponent(newQuery) + this.state.filterArgs; - } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_by_me' + this.state.filterArgs; + onAuthorPreviewItemsCountCallback(totalAuthorPreviewItems) { + this.setState({ + uploadsPreviewItemsCount: totalAuthorPreviewItems, + }); } - let title = this.state.title; + getCountFunc(count) { + this.setState( + { + channelMediaCount: count, + }, + () => { + if (this.state.query) { + let title = ''; - if ('' === newQuery) { - title = this.props.title; + if (!count) { + title = 'No results for "' + this.state.query + '"'; + } else if (1 === count) { + title = '1 result for "' + this.state.query + '"'; + } else { + title = count + ' results for "' + this.state.query + '"'; + } + + this.setState({ + title: title, + }); + } + } + ); } - this.setState({ - requestUrl: requestUrl, - query: newQuery, - title: title, - }); - } - - onToggleFiltersClick() { - this.setState({ - hiddenFilters: !this.state.hiddenFilters, - hiddenTags: true, - hiddenSorting: true, - }); - } - - onToggleTagsClick() { - this.setState({ - hiddenFilters: true, - hiddenTags: !this.state.hiddenTags, - hiddenSorting: true, - }); - } - - onToggleSortingClick() { - this.setState({ - hiddenFilters: true, - hiddenTags: true, - hiddenSorting: !this.state.hiddenSorting, - }); - } - - onTagSelect(tag) { - this.setState({ selectedTag: tag }, () => { - this.onFiltersUpdate({ - media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], - upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], - duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], - publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], - sort_by: this.state.selectedSort, - tag: tag, - }); - }); - } - - onSortSelect(sortBy) { - this.setState({ selectedSort: sortBy }, () => { - this.onFiltersUpdate({ - media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], - upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], - duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], - publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], - sort_by: sortBy, - tag: this.state.selectedTag, - }); - }); - } - - onFiltersUpdate(updatedArgs) { - const args = { - media_type: null, - upload_date: null, - duration: null, - publish_state: null, - sort_by: null, - ordering: null, - t: null, - }; - - switch (updatedArgs.media_type) { - case 'video': - case 'audio': - case 'image': - case 'pdf': - args.media_type = updatedArgs.media_type; - break; - } - - switch (updatedArgs.upload_date) { - case 'today': - case 'this_week': - case 'this_month': - case 'this_year': - args.upload_date = updatedArgs.upload_date; - break; - } - - // Handle duration filter - if (updatedArgs.duration && updatedArgs.duration !== 'all') { - args.duration = updatedArgs.duration; - } - - // Handle publish state filter - if (updatedArgs.publish_state && updatedArgs.publish_state !== 'all') { - args.publish_state = updatedArgs.publish_state; - } - - switch (updatedArgs.sort_by) { - case 'date_added_desc': - // Default sorting, no need to add parameters - break; - case 'date_added_asc': - args.ordering = 'asc'; - break; - case 'alphabetically_asc': - args.sort_by = 'title_asc'; - break; - case 'alphabetically_desc': - args.sort_by = 'title_desc'; - break; - case 'plays_least': - args.sort_by = 'views_asc'; - break; - case 'plays_most': - args.sort_by = 'views_desc'; - break; - case 'likes_least': - args.sort_by = 'likes_asc'; - break; - case 'likes_most': - args.sort_by = 'likes_desc'; - break; - } - - if (updatedArgs.tag && updatedArgs.tag !== 'all') { - args.t = updatedArgs.tag; - } - - const newArgs = []; - - for (let arg in args) { - if (null !== args[arg]) { - newArgs.push(arg + '=' + args[arg]); - } - } - - this.setState( - { - filterArgs: newArgs.length ? '&' + newArgs.join('&') : '', - }, - function () { + changeRequestQuery(newQuery) { if (!this.state.author) { - return; + return; } let requestUrl; - if (this.state.query) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_by_me&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs; + if (newQuery) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_by_me&q=' + + encodeURIComponent(newQuery) + + this.state.filterArgs; } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_by_me' + this.state.filterArgs; + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_by_me' + + this.state.filterArgs; + } + + let title = this.state.title; + + if ('' === newQuery) { + title = this.props.title; } this.setState({ - requestUrl: requestUrl, + requestUrl: requestUrl, + query: newQuery, + title: title, }); - } - ); - } - - onResponseDataLoaded(responseData) { - if (responseData && responseData.tags) { - const tags = responseData.tags.split(',').map((tag) => tag.trim()).filter((tag) => tag); - this.setState({ availableTags: tags }); } - } - pageContent() { - const authorData = ProfilePageStore.get('author-data'); + onToggleFiltersClick() { + this.setState({ + hiddenFilters: !this.state.hiddenFilters, + hiddenTags: true, + hiddenSorting: true, + }); + } - const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username; + onToggleTagsClick() { + this.setState({ + hiddenFilters: true, + hiddenTags: !this.state.hiddenTags, + hiddenSorting: true, + }); + } - // Check if any filters are active - const hasActiveFilters = this.state.filterArgs && ( - this.state.filterArgs.includes('media_type=') || - this.state.filterArgs.includes('upload_date=') || - this.state.filterArgs.includes('duration=') || - this.state.filterArgs.includes('publish_state=') - ); + onToggleSortingClick() { + this.setState({ + hiddenFilters: true, + hiddenTags: true, + hiddenSorting: !this.state.hiddenSorting, + }); + } - return [ - this.state.author ? ( - - ) : null, - this.state.author ? ( - - - - - ) : null, - this.state.author && isMediaAuthor ? ( - - ) : null, - ]; - } + onTagSelect(tag) { + this.setState({ selectedTag: tag }, () => { + this.onFiltersUpdate({ + media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], + upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], + duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], + publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], + sort_by: this.state.selectedSort, + tag: tag, + }); + }); + } + + onSortSelect(sortBy) { + this.setState({ selectedSort: sortBy }, () => { + this.onFiltersUpdate({ + media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], + upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], + duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], + publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], + sort_by: sortBy, + tag: this.state.selectedTag, + }); + }); + } + + onFiltersUpdate(updatedArgs) { + const args = { + media_type: null, + upload_date: null, + duration: null, + publish_state: null, + sort_by: null, + ordering: null, + t: null, + }; + + switch (updatedArgs.media_type) { + case 'video': + case 'audio': + case 'image': + case 'pdf': + args.media_type = updatedArgs.media_type; + break; + } + + switch (updatedArgs.upload_date) { + case 'today': + case 'this_week': + case 'this_month': + case 'this_year': + args.upload_date = updatedArgs.upload_date; + break; + } + + // Handle duration filter + if (updatedArgs.duration && updatedArgs.duration !== 'all') { + args.duration = updatedArgs.duration; + } + + // Handle publish state filter + if (updatedArgs.publish_state && updatedArgs.publish_state !== 'all') { + args.publish_state = updatedArgs.publish_state; + } + + switch (updatedArgs.sort_by) { + case 'date_added_desc': + // Default sorting, no need to add parameters + break; + case 'date_added_asc': + args.ordering = 'asc'; + break; + case 'alphabetically_asc': + args.sort_by = 'title_asc'; + break; + case 'alphabetically_desc': + args.sort_by = 'title_desc'; + break; + case 'plays_least': + args.sort_by = 'views_asc'; + break; + case 'plays_most': + args.sort_by = 'views_desc'; + break; + case 'likes_least': + args.sort_by = 'likes_asc'; + break; + case 'likes_most': + args.sort_by = 'likes_desc'; + break; + } + + if (updatedArgs.tag && updatedArgs.tag !== 'all') { + args.t = updatedArgs.tag; + } + + const newArgs = []; + + for (let arg in args) { + if (null !== args[arg]) { + newArgs.push(arg + '=' + args[arg]); + } + } + + this.setState( + { + filterArgs: newArgs.length ? '&' + newArgs.join('&') : '', + }, + function () { + if (!this.state.author) { + return; + } + + let requestUrl; + + if (this.state.query) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_by_me&q=' + + encodeURIComponent(this.state.query) + + this.state.filterArgs; + } else { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_by_me' + + this.state.filterArgs; + } + + this.setState({ + requestUrl: requestUrl, + }); + } + ); + } + + onResponseDataLoaded(responseData) { + if (responseData && responseData.tags) { + const tags = responseData.tags + .split(',') + .map((tag) => tag.trim()) + .filter((tag) => tag); + this.setState({ availableTags: tags }); + } + } + + pageContent() { + const authorData = ProfilePageStore.get('author-data'); + + const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username; + + // Check if any filters are active + const hasActiveFilters = + this.state.filterArgs && + (this.state.filterArgs.includes('media_type=') || + this.state.filterArgs.includes('upload_date=') || + this.state.filterArgs.includes('duration=') || + this.state.filterArgs.includes('publish_state=')); + + return [ + this.state.author ? ( + + ) : null, + this.state.author ? ( + + + + + ) : null, + this.state.author && isMediaAuthor ? ( + + ) : null, + ]; + } } ProfileSharedByMePage.propTypes = { - title: PropTypes.string.isRequired, - bulkActions: PropTypes.object.isRequired, + title: PropTypes.string.isRequired, + bulkActions: PropTypes.object.isRequired, }; ProfileSharedByMePage.defaultProps = { - title: 'Shared by me', + title: 'Shared by me', }; // Wrap with HOC and export as named export for compatibility diff --git a/frontend/src/static/js/pages/ProfileSharedWithMePage.js b/frontend/src/static/js/pages/ProfileSharedWithMePage.js index 9376f262..08c1f0e8 100644 --- a/frontend/src/static/js/pages/ProfileSharedWithMePage.js +++ b/frontend/src/static/js/pages/ProfileSharedWithMePage.js @@ -10,364 +10,404 @@ import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListA import { ProfileMediaFilters } from '../components/search-filters/ProfileMediaFilters'; import { ProfileMediaTags } from '../components/search-filters/ProfileMediaTags'; import { ProfileMediaSorting } from '../components/search-filters/ProfileMediaSorting'; -import { translateString } from '../utils/helpers'; +import { inEmbeddedApp, translateString } from '../utils/helpers'; import { Page } from './_Page'; import '../components/profile-page/ProfilePage.scss'; function EmptySharedWithMe(props) { - return ( - - {(links) => ( -
    -
    No shared media
    -
    - Media that others have shared with you will show up here. -
    -
    - )} -
    - ); + return ( + + {(links) => ( +
    +
    No shared media
    +
    Media that others have shared with you will show up here.
    +
    + )} +
    + ); } export class ProfileSharedWithMePage extends Page { - constructor(props, pageSlug) { - super(props, 'string' === typeof pageSlug ? pageSlug : 'author-shared-with-me'); + constructor(props, pageSlug) { + super(props, 'string' === typeof pageSlug ? pageSlug : 'author-shared-with-me'); - this.profilePageSlug = 'string' === typeof pageSlug ? pageSlug : 'author-shared-with-me'; + this.profilePageSlug = 'string' === typeof pageSlug ? pageSlug : 'author-shared-with-me'; - this.state = { - channelMediaCount: -1, - author: ProfilePageStore.get('author-data'), - uploadsPreviewItemsCount: 0, - title: this.props.title, - query: ProfilePageStore.get('author-query'), - requestUrl: null, - hiddenFilters: true, - hiddenTags: true, - hiddenSorting: true, - filterArgs: '', - availableTags: [], - selectedTag: 'all', - selectedSort: 'date_added_desc', - }; + this.state = { + channelMediaCount: -1, + author: ProfilePageStore.get('author-data'), + uploadsPreviewItemsCount: 0, + title: this.props.title, + query: ProfilePageStore.get('author-query'), + requestUrl: null, + hiddenFilters: true, + hiddenTags: true, + hiddenSorting: true, + filterArgs: '', + availableTags: [], + selectedTag: 'all', + selectedSort: 'date_added_desc', + }; - this.authorDataLoad = this.authorDataLoad.bind(this); - this.onAuthorPreviewItemsCountCallback = this.onAuthorPreviewItemsCountCallback.bind(this); - this.getCountFunc = this.getCountFunc.bind(this); - this.changeRequestQuery = this.changeRequestQuery.bind(this); - this.onToggleFiltersClick = this.onToggleFiltersClick.bind(this); - this.onToggleTagsClick = this.onToggleTagsClick.bind(this); - this.onToggleSortingClick = this.onToggleSortingClick.bind(this); - this.onFiltersUpdate = this.onFiltersUpdate.bind(this); - this.onTagSelect = this.onTagSelect.bind(this); - this.onSortSelect = this.onSortSelect.bind(this); - this.onResponseDataLoaded = this.onResponseDataLoaded.bind(this); + this.authorDataLoad = this.authorDataLoad.bind(this); + this.onAuthorPreviewItemsCountCallback = this.onAuthorPreviewItemsCountCallback.bind(this); + this.getCountFunc = this.getCountFunc.bind(this); + this.changeRequestQuery = this.changeRequestQuery.bind(this); + this.onToggleFiltersClick = this.onToggleFiltersClick.bind(this); + this.onToggleTagsClick = this.onToggleTagsClick.bind(this); + this.onToggleSortingClick = this.onToggleSortingClick.bind(this); + this.onFiltersUpdate = this.onFiltersUpdate.bind(this); + this.onTagSelect = this.onTagSelect.bind(this); + this.onSortSelect = this.onSortSelect.bind(this); + this.onResponseDataLoaded = this.onResponseDataLoaded.bind(this); - ProfilePageStore.on('load-author-data', this.authorDataLoad); - } - - componentDidMount() { - ProfilePageActions.load_author_data(); - } - - authorDataLoad() { - const author = ProfilePageStore.get('author-data'); - - let requestUrl = this.state.requestUrl; - - if (author) { - if (this.state.query) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_with_me&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs; - } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_with_me' + this.state.filterArgs; - } + ProfilePageStore.on('load-author-data', this.authorDataLoad); } - this.setState({ - author: author, - requestUrl: requestUrl, - }); - } + componentDidMount() { + ProfilePageActions.load_author_data(); + } - onAuthorPreviewItemsCountCallback(totalAuthorPreviewItems) { - this.setState({ - uploadsPreviewItemsCount: totalAuthorPreviewItems, - }); - } + authorDataLoad() { + const author = ProfilePageStore.get('author-data'); - getCountFunc(count) { - this.setState( - { - channelMediaCount: count, - }, - () => { - if (this.state.query) { - let title = ''; + let requestUrl = this.state.requestUrl; - if (!count) { - title = 'No results for "' + this.state.query + '"'; - } else if (1 === count) { - title = '1 result for "' + this.state.query + '"'; - } else { - title = count + ' results for "' + this.state.query + '"'; - } - - this.setState({ - title: title, - }); + if (author) { + if (this.state.query) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + author.id + + '&show=shared_with_me&q=' + + encodeURIComponent(this.state.query) + + this.state.filterArgs; + } else { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + author.id + + '&show=shared_with_me' + + this.state.filterArgs; + } } - } - ); - } - changeRequestQuery(newQuery) { - if (!this.state.author) { - return; + this.setState({ + author: author, + requestUrl: requestUrl, + }); } - let requestUrl; - - if (newQuery) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_with_me&q=' + encodeURIComponent(newQuery) + this.state.filterArgs; - } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_with_me' + this.state.filterArgs; + onAuthorPreviewItemsCountCallback(totalAuthorPreviewItems) { + this.setState({ + uploadsPreviewItemsCount: totalAuthorPreviewItems, + }); } - let title = this.state.title; + getCountFunc(count) { + this.setState( + { + channelMediaCount: count, + }, + () => { + if (this.state.query) { + let title = ''; - if ('' === newQuery) { - title = this.props.title; + if (!count) { + title = 'No results for "' + this.state.query + '"'; + } else if (1 === count) { + title = '1 result for "' + this.state.query + '"'; + } else { + title = count + ' results for "' + this.state.query + '"'; + } + + this.setState({ + title: title, + }); + } + } + ); } - this.setState({ - requestUrl: requestUrl, - query: newQuery, - title: title, - }); - } - - onToggleFiltersClick() { - this.setState({ - hiddenFilters: !this.state.hiddenFilters, - hiddenTags: true, - hiddenSorting: true, - }); - } - - onToggleTagsClick() { - this.setState({ - hiddenFilters: true, - hiddenTags: !this.state.hiddenTags, - hiddenSorting: true, - }); - } - - onToggleSortingClick() { - this.setState({ - hiddenFilters: true, - hiddenTags: true, - hiddenSorting: !this.state.hiddenSorting, - }); - } - - onTagSelect(tag) { - this.setState({ selectedTag: tag }, () => { - this.onFiltersUpdate({ - media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], - upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], - duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], - publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], - sort_by: this.state.selectedSort, - tag: tag, - }); - }); - } - - onSortSelect(sortBy) { - this.setState({ selectedSort: sortBy }, () => { - this.onFiltersUpdate({ - media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], - upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], - duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], - publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], - sort_by: sortBy, - tag: this.state.selectedTag, - }); - }); - } - - onFiltersUpdate(updatedArgs) { - const args = { - media_type: null, - upload_date: null, - duration: null, - publish_state: null, - sort_by: null, - ordering: null, - t: null, - }; - - switch (updatedArgs.media_type) { - case 'video': - case 'audio': - case 'image': - case 'pdf': - args.media_type = updatedArgs.media_type; - break; - } - - switch (updatedArgs.upload_date) { - case 'today': - case 'this_week': - case 'this_month': - case 'this_year': - args.upload_date = updatedArgs.upload_date; - break; - } - - // Handle duration filter - if (updatedArgs.duration && updatedArgs.duration !== 'all') { - args.duration = updatedArgs.duration; - } - - // Handle publish state filter - if (updatedArgs.publish_state && updatedArgs.publish_state !== 'all') { - args.publish_state = updatedArgs.publish_state; - } - - switch (updatedArgs.sort_by) { - case 'date_added_desc': - // Default sorting, no need to add parameters - break; - case 'date_added_asc': - args.ordering = 'asc'; - break; - case 'alphabetically_asc': - args.sort_by = 'title_asc'; - break; - case 'alphabetically_desc': - args.sort_by = 'title_desc'; - break; - case 'plays_least': - args.sort_by = 'views_asc'; - break; - case 'plays_most': - args.sort_by = 'views_desc'; - break; - case 'likes_least': - args.sort_by = 'likes_asc'; - break; - case 'likes_most': - args.sort_by = 'likes_desc'; - break; - } - - if (updatedArgs.tag && updatedArgs.tag !== 'all') { - args.t = updatedArgs.tag; - } - - const newArgs = []; - - for (let arg in args) { - if (null !== args[arg]) { - newArgs.push(arg + '=' + args[arg]); - } - } - - this.setState( - { - filterArgs: newArgs.length ? '&' + newArgs.join('&') : '', - }, - function () { + changeRequestQuery(newQuery) { if (!this.state.author) { - return; + return; } let requestUrl; - if (this.state.query) { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_with_me&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs; + if (newQuery) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_with_me&q=' + + encodeURIComponent(newQuery) + + this.state.filterArgs; } else { - requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_with_me' + this.state.filterArgs; + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_with_me' + + this.state.filterArgs; + } + + let title = this.state.title; + + if ('' === newQuery) { + title = this.props.title; } this.setState({ - requestUrl: requestUrl, + requestUrl: requestUrl, + query: newQuery, + title: title, }); - } - ); - } - - onResponseDataLoaded(responseData) { - if (responseData && responseData.tags) { - const tags = responseData.tags.split(',').map((tag) => tag.trim()).filter((tag) => tag); - this.setState({ availableTags: tags }); } - } - pageContent() { - const authorData = ProfilePageStore.get('author-data'); + onToggleFiltersClick() { + this.setState({ + hiddenFilters: !this.state.hiddenFilters, + hiddenTags: true, + hiddenSorting: true, + }); + } - const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username; + onToggleTagsClick() { + this.setState({ + hiddenFilters: true, + hiddenTags: !this.state.hiddenTags, + hiddenSorting: true, + }); + } - // Check if any filters are active - const hasActiveFilters = this.state.filterArgs && ( - this.state.filterArgs.includes('media_type=') || - this.state.filterArgs.includes('upload_date=') || - this.state.filterArgs.includes('duration=') || - this.state.filterArgs.includes('publish_state=') - ); + onToggleSortingClick() { + this.setState({ + hiddenFilters: true, + hiddenTags: true, + hiddenSorting: !this.state.hiddenSorting, + }); + } - return [ - this.state.author ? ( - - ) : null, - this.state.author ? ( - - - - - ) : null, - ]; - } + onTagSelect(tag) { + this.setState({ selectedTag: tag }, () => { + this.onFiltersUpdate({ + media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], + upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], + duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], + publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], + sort_by: this.state.selectedSort, + tag: tag, + }); + }); + } + + onSortSelect(sortBy) { + this.setState({ selectedSort: sortBy }, () => { + this.onFiltersUpdate({ + media_type: this.state.filterArgs.match(/media_type=([^&]+)/)?.[1], + upload_date: this.state.filterArgs.match(/upload_date=([^&]+)/)?.[1], + duration: this.state.filterArgs.match(/duration=([^&]+)/)?.[1], + publish_state: this.state.filterArgs.match(/publish_state=([^&]+)/)?.[1], + sort_by: sortBy, + tag: this.state.selectedTag, + }); + }); + } + + onFiltersUpdate(updatedArgs) { + const args = { + media_type: null, + upload_date: null, + duration: null, + publish_state: null, + sort_by: null, + ordering: null, + t: null, + }; + + switch (updatedArgs.media_type) { + case 'video': + case 'audio': + case 'image': + case 'pdf': + args.media_type = updatedArgs.media_type; + break; + } + + switch (updatedArgs.upload_date) { + case 'today': + case 'this_week': + case 'this_month': + case 'this_year': + args.upload_date = updatedArgs.upload_date; + break; + } + + // Handle duration filter + if (updatedArgs.duration && updatedArgs.duration !== 'all') { + args.duration = updatedArgs.duration; + } + + // Handle publish state filter + if (updatedArgs.publish_state && updatedArgs.publish_state !== 'all') { + args.publish_state = updatedArgs.publish_state; + } + + switch (updatedArgs.sort_by) { + case 'date_added_desc': + // Default sorting, no need to add parameters + break; + case 'date_added_asc': + args.ordering = 'asc'; + break; + case 'alphabetically_asc': + args.sort_by = 'title_asc'; + break; + case 'alphabetically_desc': + args.sort_by = 'title_desc'; + break; + case 'plays_least': + args.sort_by = 'views_asc'; + break; + case 'plays_most': + args.sort_by = 'views_desc'; + break; + case 'likes_least': + args.sort_by = 'likes_asc'; + break; + case 'likes_most': + args.sort_by = 'likes_desc'; + break; + } + + if (updatedArgs.tag && updatedArgs.tag !== 'all') { + args.t = updatedArgs.tag; + } + + const newArgs = []; + + for (let arg in args) { + if (null !== args[arg]) { + newArgs.push(arg + '=' + args[arg]); + } + } + + this.setState( + { + filterArgs: newArgs.length ? '&' + newArgs.join('&') : '', + }, + function () { + if (!this.state.author) { + return; + } + + let requestUrl; + + if (this.state.query) { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_with_me&q=' + + encodeURIComponent(this.state.query) + + this.state.filterArgs; + } else { + requestUrl = + ApiUrlContext._currentValue.media + + '?author=' + + this.state.author.id + + '&show=shared_with_me' + + this.state.filterArgs; + } + + this.setState({ + requestUrl: requestUrl, + }); + } + ); + } + + onResponseDataLoaded(responseData) { + if (responseData && responseData.tags) { + const tags = responseData.tags + .split(',') + .map((tag) => tag.trim()) + .filter((tag) => tag); + this.setState({ availableTags: tags }); + } + } + + pageContent() { + const authorData = ProfilePageStore.get('author-data'); + + const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username; + + // Check if any filters are active + const hasActiveFilters = + this.state.filterArgs && + (this.state.filterArgs.includes('media_type=') || + this.state.filterArgs.includes('upload_date=') || + this.state.filterArgs.includes('duration=') || + this.state.filterArgs.includes('publish_state=')); + + return [ + this.state.author ? ( + + ) : null, + this.state.author ? ( + + + + + ) : null, + ]; + } } ProfileSharedWithMePage.propTypes = { - title: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, }; ProfileSharedWithMePage.defaultProps = { - title: 'Shared with me', + title: 'Shared with me', }; diff --git a/frontend/src/static/js/pages/_MediaPage.js b/frontend/src/static/js/pages/_MediaPage.js index c5e4a4ed..0ed50174 100755 --- a/frontend/src/static/js/pages/_MediaPage.js +++ b/frontend/src/static/js/pages/_MediaPage.js @@ -1,6 +1,7 @@ import React from 'react'; import { PageStore, MediaPageStore } from '../utils/stores/'; import { MediaPageActions } from '../utils/actions/'; +import { inEmbeddedApp } from '../utils/helpers/'; import ViewerError from '../components/media-page/ViewerError'; import ViewerInfo from '../components/media-page/ViewerInfo'; import ViewerSidebar from '../components/media-page/ViewerSidebar'; @@ -10,102 +11,102 @@ import '../components/media-page/MediaPage.scss'; const wideLayoutBreakpoint = 1216; export class _MediaPage extends Page { - constructor(props) { - super(props, 'media'); + constructor(props) { + super(props, 'media'); - const isWideLayout = wideLayoutBreakpoint <= window.innerWidth; + const isWideLayout = wideLayoutBreakpoint <= window.innerWidth; - this.state = { - mediaLoaded: false, - mediaLoadFailed: false, - wideLayout: isWideLayout, - infoAndSidebarViewType: !isWideLayout ? 0 : 1, - viewerClassname: 'cf viewer-section viewer-wide', - viewerNestedClassname: 'viewer-section-nested', - pagePlaylistLoaded: false, - }; + this.state = { + mediaLoaded: false, + mediaLoadFailed: false, + wideLayout: isWideLayout, + infoAndSidebarViewType: !isWideLayout ? 0 : 1, + viewerClassname: 'cf viewer-section viewer-wide', + viewerNestedClassname: 'viewer-section-nested', + pagePlaylistLoaded: false, + }; - this.onWindowResize = this.onWindowResize.bind(this); - this.onMediaLoad = this.onMediaLoad.bind(this); - this.onMediaLoadError = this.onMediaLoadError.bind(this); - this.onPagePlaylistLoad = this.onPagePlaylistLoad.bind(this); + this.onWindowResize = this.onWindowResize.bind(this); + this.onMediaLoad = this.onMediaLoad.bind(this); + this.onMediaLoadError = this.onMediaLoadError.bind(this); + this.onPagePlaylistLoad = this.onPagePlaylistLoad.bind(this); - MediaPageStore.on('loaded_media_data', this.onMediaLoad); - MediaPageStore.on('loaded_media_error', this.onMediaLoadError); - MediaPageStore.on('loaded_page_playlist_data', this.onPagePlaylistLoad); - } + MediaPageStore.on('loaded_media_data', this.onMediaLoad); + MediaPageStore.on('loaded_media_error', this.onMediaLoadError); + MediaPageStore.on('loaded_page_playlist_data', this.onPagePlaylistLoad); + } - componentDidMount() { - MediaPageActions.loadMediaData(); - // FIXME: Is not neccessary to check on every window dimension for changes... - PageStore.on('window_resize', this.onWindowResize); - } + componentDidMount() { + MediaPageActions.loadMediaData(); + // FIXME: Is not neccessary to check on every window dimension for changes... + PageStore.on('window_resize', this.onWindowResize); + } - onPagePlaylistLoad() { - this.setState({ - pagePlaylistLoaded: true, - }); - } + onPagePlaylistLoad() { + this.setState({ + pagePlaylistLoaded: true, + }); + } - onWindowResize() { - const isWideLayout = wideLayoutBreakpoint <= window.innerWidth; + onWindowResize() { + const isWideLayout = wideLayoutBreakpoint <= window.innerWidth; - this.setState({ - wideLayout: isWideLayout, - infoAndSidebarViewType: !isWideLayout || (MediaPageStore.isVideo() && this.state.theaterMode) ? 0 : 1, - }); - } + this.setState({ + wideLayout: isWideLayout, + infoAndSidebarViewType: !isWideLayout || (MediaPageStore.isVideo() && this.state.theaterMode) ? 0 : 1, + }); + } - onMediaLoad() { - this.setState({ mediaLoaded: true }); - } + onMediaLoad() { + this.setState({ mediaLoaded: true }); + } - onMediaLoadError() { - this.setState({ mediaLoadFailed: true }); - } + onMediaLoadError() { + this.setState({ mediaLoadFailed: true }); + } - viewerContainerContent() { - return null; - } + viewerContainerContent() { + return null; + } - mediaType() { - return null; - } + mediaType() { + return null; + } - pageContent() { - return this.state.mediaLoadFailed ? ( -
    - -
    - ) : ( -
    -
    - {this.state.mediaLoaded ? this.viewerContainerContent() : null} -
    -
    - {!this.state.infoAndSidebarViewType - ? [ - , - this.state.pagePlaylistLoaded ? ( - - ) : null, - ] - : [ - this.state.pagePlaylistLoaded ? ( - - ) : null, - , - ]} -
    -
    - ); - } + pageContent() { + return this.state.mediaLoadFailed ? ( +
    + +
    + ) : ( +
    +
    + {this.state.mediaLoaded ? this.viewerContainerContent() : null} +
    +
    + {!this.state.infoAndSidebarViewType + ? [ + , + !inEmbeddedApp() && this.state.pagePlaylistLoaded ? ( + + ) : null, + ] + : [ + !inEmbeddedApp() && this.state.pagePlaylistLoaded ? ( + + ) : null, + , + ]} +
    +
    + ); + } } diff --git a/frontend/src/static/js/pages/_VideoMediaPage.js b/frontend/src/static/js/pages/_VideoMediaPage.js index eed1be70..6c052145 100644 --- a/frontend/src/static/js/pages/_VideoMediaPage.js +++ b/frontend/src/static/js/pages/_VideoMediaPage.js @@ -2,6 +2,7 @@ import React from 'react'; // FIXME: 'VideoViewerStore' is used only in case of video media, but is included in every media page code. import { PageStore, MediaPageStore, VideoViewerStore } from '../utils/stores/'; import { MediaPageActions } from '../utils/actions/'; +import { inEmbeddedApp } from '../utils/helpers/'; import ViewerInfoVideo from '../components/media-page/ViewerInfoVideo'; import ViewerError from '../components/media-page/ViewerError'; import ViewerSidebar from '../components/media-page/ViewerSidebar'; @@ -11,118 +12,119 @@ import _MediaPage from './_MediaPage'; const wideLayoutBreakpoint = 1216; export class _VideoMediaPage extends Page { - constructor(props) { - super(props, 'media'); + constructor(props) { + super(props, 'media'); - this.state = { - wideLayout: wideLayoutBreakpoint <= window.innerWidth, - mediaLoaded: false, - mediaLoadFailed: false, - isVideoMedia: false, - theaterMode: false, // FIXME: Used only in case of video media, but is included in every media page code. - pagePlaylistLoaded: false, - pagePlaylistData: MediaPageStore.get('playlist-data'), - }; + this.state = { + wideLayout: wideLayoutBreakpoint <= window.innerWidth, + mediaLoaded: false, + mediaLoadFailed: false, + isVideoMedia: false, + theaterMode: false, // FIXME: Used only in case of video media, but is included in every media page code. + pagePlaylistLoaded: false, + pagePlaylistData: MediaPageStore.get('playlist-data'), + }; - this.onWindowResize = this.onWindowResize.bind(this); - this.onMediaLoad = this.onMediaLoad.bind(this); - this.onMediaLoadError = this.onMediaLoadError.bind(this); - this.onPagePlaylistLoad = this.onPagePlaylistLoad.bind(this); + this.onWindowResize = this.onWindowResize.bind(this); + this.onMediaLoad = this.onMediaLoad.bind(this); + this.onMediaLoadError = this.onMediaLoadError.bind(this); + this.onPagePlaylistLoad = this.onPagePlaylistLoad.bind(this); - MediaPageStore.on('loaded_media_data', this.onMediaLoad); - MediaPageStore.on('loaded_media_error', this.onMediaLoadError); - MediaPageStore.on('loaded_page_playlist_data', this.onPagePlaylistLoad); - } - - componentDidMount() { - MediaPageActions.loadMediaData(); - // FIXME: Is not neccessary to check on every window dimension for changes... - PageStore.on('window_resize', this.onWindowResize); - } - - onWindowResize() { - this.setState({ - wideLayout: wideLayoutBreakpoint <= window.innerWidth, - }); - } - - onPagePlaylistLoad() { - this.setState({ - pagePlaylistLoaded: true, - pagePlaylistData: MediaPageStore.get('playlist-data'), - }); - } - - onMediaLoad() { - const isVideoMedia = 'video' === MediaPageStore.get('media-type') || 'audio' === MediaPageStore.get('media-type'); - - if (isVideoMedia) { - this.onViewerModeChange = this.onViewerModeChange.bind(this); - - VideoViewerStore.on('changed_viewer_mode', this.onViewerModeChange); - - this.setState({ - mediaLoaded: true, - isVideoMedia: isVideoMedia, - theaterMode: VideoViewerStore.get('in-theater-mode'), - }); - } else { - this.setState({ - mediaLoaded: true, - isVideoMedia: isVideoMedia, - }); + MediaPageStore.on('loaded_media_data', this.onMediaLoad); + MediaPageStore.on('loaded_media_error', this.onMediaLoadError); + MediaPageStore.on('loaded_page_playlist_data', this.onPagePlaylistLoad); } - } - onViewerModeChange() { - this.setState({ theaterMode: VideoViewerStore.get('in-theater-mode') }); - } + componentDidMount() { + MediaPageActions.loadMediaData(); + // FIXME: Is not neccessary to check on every window dimension for changes... + PageStore.on('window_resize', this.onWindowResize); + } - onMediaLoadError(a) { - this.setState({ mediaLoadFailed: true }); - } + onWindowResize() { + this.setState({ + wideLayout: wideLayoutBreakpoint <= window.innerWidth, + }); + } - pageContent() { - const viewerClassname = 'cf viewer-section' + (this.state.theaterMode ? ' theater-mode' : ' viewer-wide'); - const viewerNestedClassname = 'viewer-section-nested' + (this.state.theaterMode ? ' viewer-section' : ''); + onPagePlaylistLoad() { + this.setState({ + pagePlaylistLoaded: true, + pagePlaylistData: MediaPageStore.get('playlist-data'), + }); + } - return this.state.mediaLoadFailed ? ( -
    - -
    - ) : ( -
    - {[ -
    - {this.state.mediaLoaded && this.state.pagePlaylistLoaded - ? this.viewerContainerContent(MediaPageStore.get('media-data')) - : null} -
    , -
    - {!this.state.wideLayout || (this.state.isVideoMedia && this.state.theaterMode) - ? [ - , - this.state.pagePlaylistLoaded ? ( - - ) : null, - ] - : [ - this.state.pagePlaylistLoaded ? ( - - ) : null, - , + onMediaLoad() { + const isVideoMedia = + 'video' === MediaPageStore.get('media-type') || 'audio' === MediaPageStore.get('media-type'); + + if (isVideoMedia) { + this.onViewerModeChange = this.onViewerModeChange.bind(this); + + VideoViewerStore.on('changed_viewer_mode', this.onViewerModeChange); + + this.setState({ + mediaLoaded: true, + isVideoMedia: isVideoMedia, + theaterMode: VideoViewerStore.get('in-theater-mode'), + }); + } else { + this.setState({ + mediaLoaded: true, + isVideoMedia: isVideoMedia, + }); + } + } + + onViewerModeChange() { + this.setState({ theaterMode: VideoViewerStore.get('in-theater-mode') }); + } + + onMediaLoadError(a) { + this.setState({ mediaLoadFailed: true }); + } + + pageContent() { + const viewerClassname = 'cf viewer-section' + (this.state.theaterMode ? ' theater-mode' : ' viewer-wide'); + const viewerNestedClassname = 'viewer-section-nested' + (this.state.theaterMode ? ' viewer-section' : ''); + + return this.state.mediaLoadFailed ? ( +
    + +
    + ) : ( +
    + {[ +
    + {this.state.mediaLoaded && this.state.pagePlaylistLoaded + ? this.viewerContainerContent(MediaPageStore.get('media-data')) + : null} +
    , +
    + {!this.state.wideLayout || (this.state.isVideoMedia && this.state.theaterMode) + ? [ + , + !inEmbeddedApp() && this.state.pagePlaylistLoaded ? ( + + ) : null, + ] + : [ + !inEmbeddedApp() && this.state.pagePlaylistLoaded ? ( + + ) : null, + , + ]} +
    , ]} -
    , - ]} -
    - ); - } +
    + ); + } } diff --git a/frontend/src/static/js/utils/contexts/LayoutContext.js b/frontend/src/static/js/utils/contexts/LayoutContext.js index 52f59b5f..813fc80b 100644 --- a/frontend/src/static/js/utils/contexts/LayoutContext.js +++ b/frontend/src/static/js/utils/contexts/LayoutContext.js @@ -1,101 +1,103 @@ -import React, { createContext, useContext, useEffect, useState } from 'react'; +import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { BrowserCache } from '../classes/'; import { PageStore } from '../stores/'; -import { addClassname, removeClassname } from '../helpers/'; +import { addClassname, removeClassname, inEmbeddedApp } from '../helpers/'; import SiteContext from './SiteContext'; let slidingSidebarTimeout; function onSidebarVisibilityChange(visibleSidebar) { - clearTimeout(slidingSidebarTimeout); + clearTimeout(slidingSidebarTimeout); - addClassname(document.body, 'sliding-sidebar'); - - slidingSidebarTimeout = setTimeout(function () { - if ('media' === PageStore.get('current-page')) { - if (visibleSidebar) { - addClassname(document.body, 'overflow-hidden'); - } else { - removeClassname(document.body, 'overflow-hidden'); - } - } else { - if (!visibleSidebar || 767 < window.innerWidth) { - removeClassname(document.body, 'overflow-hidden'); - } else { - addClassname(document.body, 'overflow-hidden'); - } - } - - if (visibleSidebar) { - addClassname(document.body, 'visible-sidebar'); - } else { - removeClassname(document.body, 'visible-sidebar'); - } + addClassname(document.body, 'sliding-sidebar'); slidingSidebarTimeout = setTimeout(function () { - slidingSidebarTimeout = null; - removeClassname(document.body, 'sliding-sidebar'); - }, 220); - }, 20); + if ('media' === PageStore.get('current-page')) { + if (visibleSidebar) { + addClassname(document.body, 'overflow-hidden'); + } else { + removeClassname(document.body, 'overflow-hidden'); + } + } else { + if (!visibleSidebar || 767 < window.innerWidth) { + removeClassname(document.body, 'overflow-hidden'); + } else { + addClassname(document.body, 'overflow-hidden'); + } + } + + if (visibleSidebar) { + addClassname(document.body, 'visible-sidebar'); + } else { + removeClassname(document.body, 'visible-sidebar'); + } + + slidingSidebarTimeout = setTimeout(function () { + slidingSidebarTimeout = null; + removeClassname(document.body, 'sliding-sidebar'); + }, 220); + }, 20); } export const LayoutContext = createContext(); export const LayoutProvider = ({ children }) => { - const site = useContext(SiteContext); - const cache = new BrowserCache('MediaCMS[' + site.id + '][layout]', 86400); + const site = useContext(SiteContext); + const cache = new BrowserCache('MediaCMS[' + site.id + '][layout]', 86400); - const enabledSidebar = !!(document.getElementById('app-sidebar') || document.querySelector('.page-sidebar')); + const isMediaPage = useMemo(() => PageStore.get('current-page') === 'media', []); + const isEmbeddedApp = useMemo(() => inEmbeddedApp(), []); - const [visibleSidebar, setVisibleSidebar] = useState(cache.get('visible-sidebar')); - const [visibleMobileSearch, setVisibleMobileSearch] = useState(false); + const enabledSidebar = Boolean(document.getElementById('app-sidebar') || document.querySelector('.page-sidebar')); - const toggleMobileSearch = () => { - setVisibleMobileSearch(!visibleMobileSearch); - }; + const [visibleSidebar, setVisibleSidebar] = useState(cache.get('visible-sidebar')); + const [visibleMobileSearch, setVisibleMobileSearch] = useState(false); - const toggleSidebar = () => { - const newval = !visibleSidebar; - onSidebarVisibilityChange(newval); - setVisibleSidebar(newval); - }; + const toggleMobileSearch = () => { + setVisibleMobileSearch(!visibleMobileSearch); + }; - useEffect(() => { - if (visibleSidebar) { - addClassname(document.body, 'visible-sidebar'); - } else { - removeClassname(document.body, 'visible-sidebar'); - } - if ('media' !== PageStore.get('current-page') && 1023 < window.innerWidth) { - cache.set('visible-sidebar', visibleSidebar); - } - }, [visibleSidebar]); + const toggleSidebar = () => { + const newval = !visibleSidebar; + onSidebarVisibilityChange(newval); + setVisibleSidebar(newval); + }; - useEffect(() => { - PageStore.once('page_init', () => { - if ('media' === PageStore.get('current-page')) { - setVisibleSidebar(false); - removeClassname(document.body, 'visible-sidebar'); - } - }); + useEffect(() => { + if (!isEmbeddedApp && visibleSidebar) { + addClassname(document.body, 'visible-sidebar'); + } else { + removeClassname(document.body, 'visible-sidebar'); + } - setVisibleSidebar( - 'media' !== PageStore.get('current-page') && - 1023 < window.innerWidth && - (null === visibleSidebar || visibleSidebar) - ); - }, []); + if (!isEmbeddedApp && !isMediaPage && 1023 < window.innerWidth) { + cache.set('visible-sidebar', visibleSidebar); + } + }, [isEmbeddedApp, isMediaPage, visibleSidebar]); - const value = { - enabledSidebar, - visibleSidebar, - setVisibleSidebar, - visibleMobileSearch, - toggleMobileSearch, - toggleSidebar, - }; + useEffect(() => { + PageStore.once('page_init', () => { + if (isEmbeddedApp || isMediaPage) { + setVisibleSidebar(false); + removeClassname(document.body, 'visible-sidebar'); + } + }); - return {children}; + setVisibleSidebar( + !isEmbeddedApp && !isMediaPage && 1023 < window.innerWidth && (null === visibleSidebar || visibleSidebar) + ); + }, []); + + const value = { + enabledSidebar, + visibleSidebar, + setVisibleSidebar, + visibleMobileSearch, + toggleMobileSearch, + toggleSidebar, + }; + + return {children}; }; export const LayoutConsumer = LayoutContext.Consumer; diff --git a/frontend/src/static/js/utils/helpers/embeddedApp.ts b/frontend/src/static/js/utils/helpers/embeddedApp.ts new file mode 100644 index 00000000..7c859f77 --- /dev/null +++ b/frontend/src/static/js/utils/helpers/embeddedApp.ts @@ -0,0 +1,4 @@ +export function inEmbeddedApp() { + const url = new URL(globalThis.location.href); + return url.searchParams.get('mode') === 'embed_mode'; +} diff --git a/frontend/src/static/js/utils/helpers/index.js b/frontend/src/static/js/utils/helpers/index.js index 3b69683f..0376b929 100644 --- a/frontend/src/static/js/utils/helpers/index.js +++ b/frontend/src/static/js/utils/helpers/index.js @@ -14,3 +14,4 @@ export * from './quickSort'; export * from './requests'; export { translateString } from './translate'; export { replaceString } from './replacementStrings'; +export * from './embeddedApp'; diff --git a/frontend/src/static/js/utils/renderer.js b/frontend/src/static/js/utils/renderer.js index 7e663b6a..1eeaa484 100755 --- a/frontend/src/static/js/utils/renderer.js +++ b/frontend/src/static/js/utils/renderer.js @@ -3,64 +3,82 @@ import ReactDOM from 'react-dom'; import { ThemeProvider } from './contexts/ThemeContext'; import { LayoutProvider } from './contexts/LayoutContext'; import { UserProvider } from './contexts/UserContext'; +import { inEmbeddedApp } from './helpers'; const AppProviders = ({ children }) => ( - - - {children} - - + + + {children} + + ); import { PageHeader, PageSidebar } from '../components/page-layout'; export function renderPage(idSelector, PageComponent) { - const appHeader = document.getElementById('app-header'); - const appSidebar = document.getElementById('app-sidebar'); - const appContent = idSelector ? document.getElementById(idSelector) : undefined; + const appContent = idSelector ? document.getElementById(idSelector) : undefined; - if (appContent && PageComponent) { - ReactDOM.render( - - {appHeader ? ReactDOM.createPortal(, appHeader) : null} - {appSidebar ? ReactDOM.createPortal(, appSidebar) : null} - - , - appContent - ); - } else if (appHeader && appSidebar) { - ReactDOM.render( - - {ReactDOM.createPortal(, appHeader)} - - , - appSidebar - ); - } else if (appHeader) { - ReactDOM.render( - - - - - - - , - appSidebar - ); - } else if (appSidebar) { - ReactDOM.render( - - - , - appSidebar - ); - } + if (inEmbeddedApp() && appContent) { + globalThis.document.body.classList.add('embedded-app'); + globalThis.document.body.classList.remove('visible-sidebar'); + + if (PageComponent) { + ReactDOM.render( + + + , + appContent + ); + } + + return; + } + + const appHeader = document.getElementById('app-header'); + const appSidebar = document.getElementById('app-sidebar'); + + if (appContent && PageComponent) { + ReactDOM.render( + + {appHeader ? ReactDOM.createPortal(, appHeader) : null} + {appSidebar ? ReactDOM.createPortal(, appSidebar) : null} + + , + appContent + ); + } else if (appHeader && appSidebar) { + ReactDOM.render( + + {ReactDOM.createPortal(, appHeader)} + + , + appSidebar + ); + } else if (appHeader) { + ReactDOM.render( + + + + + + + , + appSidebar + ); + } else if (appSidebar) { + ReactDOM.render( + + + , + appSidebar + ); + } } export function renderEmbedPage(idSelector, PageComponent) { - const appContent = idSelector ? document.getElementById(idSelector) : undefined; + const appContent = idSelector ? document.getElementById(idSelector) : undefined; - if (appContent && PageComponent) { - ReactDOM.render(, appContent); - } + if (appContent && PageComponent) { + ReactDOM.render(, appContent); + } }