Compare commits
1 Commits
feat/video
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
223e87073f |
@ -1 +1 @@
|
|||||||
VERSION = "7.6"
|
VERSION = "7.7"
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" style="height: 100%">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Embedded Video - Full Screen</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: #000;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<iframe
|
|
||||||
src="https://demo.mediacms.io/embed?m=zK2nirNLC"
|
|
||||||
style="
|
|
||||||
width: 100%;
|
|
||||||
max-width: calc(100vh * 16 / 9);
|
|
||||||
aspect-ratio: 16 / 9;
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
border: 0;
|
|
||||||
"
|
|
||||||
allowfullscreen
|
|
||||||
></iframe>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -204,54 +204,6 @@ class SeekIndicator extends Component {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
textEl.textContent = 'Pause';
|
textEl.textContent = 'Pause';
|
||||||
} else if (direction === 'copy-url') {
|
|
||||||
iconEl.innerHTML = `
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center; animation: youtubeSeekPulse 0.3s ease-out;">
|
|
||||||
<div style="
|
|
||||||
width: ${circleSize};
|
|
||||||
height: ${circleSize};
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
">
|
|
||||||
<svg viewBox="0 0 24 24" width="${iconSize}" height="${iconSize}" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style="filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));">
|
|
||||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
|
||||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
textEl.textContent = '';
|
|
||||||
} else if (direction === 'copy-embed') {
|
|
||||||
iconEl.innerHTML = `
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center; animation: youtubeSeekPulse 0.3s ease-out;">
|
|
||||||
<div style="
|
|
||||||
width: ${circleSize};
|
|
||||||
height: ${circleSize};
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
">
|
|
||||||
<svg viewBox="0 0 24 24" width="${iconSize}" height="${iconSize}" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style="filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));">
|
|
||||||
<path d="M16 18l6-6-6-6"/>
|
|
||||||
<path d="M8 6l-6 6 6 6"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
textEl.textContent = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear any text content in the text element
|
// Clear any text content in the text element
|
||||||
@ -287,11 +239,6 @@ class SeekIndicator extends Component {
|
|||||||
this.showTimeout = setTimeout(() => {
|
this.showTimeout = setTimeout(() => {
|
||||||
this.hide();
|
this.hide();
|
||||||
}, 500);
|
}, 500);
|
||||||
} else if (direction === 'copy-url' || direction === 'copy-embed') {
|
|
||||||
// Copy operations: 500ms (same as play/pause)
|
|
||||||
this.showTimeout = setTimeout(() => {
|
|
||||||
this.hide();
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,22 +14,10 @@ class EmbedInfoOverlay extends Component {
|
|||||||
this.authorThumbnail = options.authorThumbnail || '';
|
this.authorThumbnail = options.authorThumbnail || '';
|
||||||
this.videoTitle = options.videoTitle || 'Video';
|
this.videoTitle = options.videoTitle || 'Video';
|
||||||
this.videoUrl = options.videoUrl || '';
|
this.videoUrl = options.videoUrl || '';
|
||||||
this.showTitle = options.showTitle !== undefined ? options.showTitle : true;
|
|
||||||
this.showRelated = options.showRelated !== undefined ? options.showRelated : true;
|
|
||||||
this.showUserAvatar = options.showUserAvatar !== undefined ? options.showUserAvatar : true;
|
|
||||||
this.linkTitle = options.linkTitle !== undefined ? options.linkTitle : true;
|
|
||||||
|
|
||||||
// Initialize after player is ready
|
// Initialize after player is ready
|
||||||
this.player().ready(() => {
|
this.player().ready(() => {
|
||||||
if (this.showTitle) {
|
|
||||||
this.createOverlay();
|
this.createOverlay();
|
||||||
} else {
|
|
||||||
// Hide overlay element if showTitle is false
|
|
||||||
const overlay = this.el();
|
|
||||||
overlay.style.display = 'none';
|
|
||||||
overlay.style.opacity = '0';
|
|
||||||
overlay.style.visibility = 'hidden';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +49,7 @@ class EmbedInfoOverlay extends Component {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
// Create avatar container
|
// Create avatar container
|
||||||
if (this.authorThumbnail && this.showUserAvatar) {
|
if (this.authorThumbnail) {
|
||||||
const avatarContainer = document.createElement('div');
|
const avatarContainer = document.createElement('div');
|
||||||
avatarContainer.className = 'embed-avatar-container';
|
avatarContainer.className = 'embed-avatar-container';
|
||||||
avatarContainer.style.cssText = `
|
avatarContainer.style.cssText = `
|
||||||
@ -137,7 +125,7 @@ class EmbedInfoOverlay extends Component {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (this.videoUrl && this.linkTitle) {
|
if (this.videoUrl) {
|
||||||
const titleLink = document.createElement('a');
|
const titleLink = document.createElement('a');
|
||||||
titleLink.href = this.videoUrl;
|
titleLink.href = this.videoUrl;
|
||||||
titleLink.target = '_blank';
|
titleLink.target = '_blank';
|
||||||
@ -198,16 +186,10 @@ class EmbedInfoOverlay extends Component {
|
|||||||
const player = this.player();
|
const player = this.player();
|
||||||
const overlay = this.el();
|
const overlay = this.el();
|
||||||
|
|
||||||
// If showTitle is false, ensure overlay is hidden
|
|
||||||
if (!this.showTitle) {
|
|
||||||
overlay.style.display = 'none';
|
|
||||||
overlay.style.opacity = '0';
|
|
||||||
overlay.style.visibility = 'hidden';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync overlay visibility with control bar visibility
|
// Sync overlay visibility with control bar visibility
|
||||||
const updateOverlayVisibility = () => {
|
const updateOverlayVisibility = () => {
|
||||||
|
const controlBar = player.getChild('controlBar');
|
||||||
|
|
||||||
if (!player.hasStarted()) {
|
if (!player.hasStarted()) {
|
||||||
// Show overlay when video hasn't started (poster is showing) - like before
|
// Show overlay when video hasn't started (poster is showing) - like before
|
||||||
overlay.style.opacity = '1';
|
overlay.style.opacity = '1';
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
.video-context-menu {
|
|
||||||
position: fixed;
|
|
||||||
background-color: #282828;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 4px 0;
|
|
||||||
min-width: 240px;
|
|
||||||
z-index: 10000;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-context-menu-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.15s ease;
|
|
||||||
font-size: 14px;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-context-menu-item:hover {
|
|
||||||
background-color: #3d3d3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-context-menu-item:active {
|
|
||||||
background-color: #4a4a4a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-context-menu-icon {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 12px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
stroke: currentColor;
|
|
||||||
fill: none;
|
|
||||||
stroke-width: 2;
|
|
||||||
stroke-linecap: round;
|
|
||||||
stroke-linejoin: round;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-context-menu-item span {
|
|
||||||
flex: 1;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
|
||||||
import './VideoContextMenu.css';
|
|
||||||
|
|
||||||
function VideoContextMenu({ visible, position, onClose, onCopyVideoUrl, onCopyVideoUrlAtTime, onCopyEmbedCode }) {
|
|
||||||
const menuRef = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible && menuRef.current) {
|
|
||||||
// Position the menu
|
|
||||||
menuRef.current.style.left = `${position.x}px`;
|
|
||||||
menuRef.current.style.top = `${position.y}px`;
|
|
||||||
|
|
||||||
// Adjust if menu goes off screen
|
|
||||||
const rect = menuRef.current.getBoundingClientRect();
|
|
||||||
const windowWidth = window.innerWidth;
|
|
||||||
const windowHeight = window.innerHeight;
|
|
||||||
|
|
||||||
if (rect.right > windowWidth) {
|
|
||||||
menuRef.current.style.left = `${position.x - rect.width}px`;
|
|
||||||
}
|
|
||||||
if (rect.bottom > windowHeight) {
|
|
||||||
menuRef.current.style.top = `${position.y - rect.height}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [visible, position]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleClickOutside = (e) => {
|
|
||||||
if (visible && menuRef.current && !menuRef.current.contains(e.target)) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEscape = (e) => {
|
|
||||||
if (e.key === 'Escape' && visible) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
// Use capture phase to catch events earlier, before they can be stopped
|
|
||||||
// Listen to both mousedown and click to ensure we catch all clicks
|
|
||||||
document.addEventListener('mousedown', handleClickOutside, true);
|
|
||||||
document.addEventListener('click', handleClickOutside, true);
|
|
||||||
document.addEventListener('keydown', handleEscape);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mousedown', handleClickOutside, true);
|
|
||||||
document.removeEventListener('click', handleClickOutside, true);
|
|
||||||
document.removeEventListener('keydown', handleEscape);
|
|
||||||
};
|
|
||||||
}, [visible, onClose]);
|
|
||||||
|
|
||||||
if (!visible) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={menuRef} className="video-context-menu" onClick={(e) => e.stopPropagation()}>
|
|
||||||
<div className="video-context-menu-item" onClick={onCopyVideoUrl}>
|
|
||||||
<svg className="video-context-menu-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span>Copy video URL</span>
|
|
||||||
</div>
|
|
||||||
<div className="video-context-menu-item" onClick={onCopyVideoUrlAtTime}>
|
|
||||||
<svg className="video-context-menu-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span>Copy video URL at current time</span>
|
|
||||||
</div>
|
|
||||||
<div className="video-context-menu-item" onClick={onCopyEmbedCode}>
|
|
||||||
<svg className="video-context-menu-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M16 18l6-6-6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
<path d="M8 6l-6 6 6 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span>Copy embed code</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default VideoContextMenu;
|
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useRef, useMemo, useState, useCallback } from 'react';
|
import React, { useEffect, useRef, useMemo } from 'react';
|
||||||
import videojs from 'video.js';
|
import videojs from 'video.js';
|
||||||
import 'video.js/dist/video-js.css';
|
import 'video.js/dist/video-js.css';
|
||||||
import '../../styles/embed.css';
|
import '../../styles/embed.css';
|
||||||
@ -17,7 +17,6 @@ import CustomRemainingTime from '../controls/CustomRemainingTime';
|
|||||||
import CustomChaptersOverlay from '../controls/CustomChaptersOverlay';
|
import CustomChaptersOverlay from '../controls/CustomChaptersOverlay';
|
||||||
import CustomSettingsMenu from '../controls/CustomSettingsMenu';
|
import CustomSettingsMenu from '../controls/CustomSettingsMenu';
|
||||||
import SeekIndicator from '../controls/SeekIndicator';
|
import SeekIndicator from '../controls/SeekIndicator';
|
||||||
import VideoContextMenu from '../overlays/VideoContextMenu';
|
|
||||||
import UserPreferences from '../../utils/UserPreferences';
|
import UserPreferences from '../../utils/UserPreferences';
|
||||||
import PlayerConfig from '../../config/playerConfig';
|
import PlayerConfig from '../../config/playerConfig';
|
||||||
import { AutoplayHandler } from '../../utils/AutoplayHandler';
|
import { AutoplayHandler } from '../../utils/AutoplayHandler';
|
||||||
@ -170,7 +169,7 @@ const enableStandardButtonTooltips = (player) => {
|
|||||||
}, 500); // Delay to ensure all components are ready
|
}, 500); // Delay to ensure all components are ready
|
||||||
};
|
};
|
||||||
|
|
||||||
function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelated = true, showUserAvatar = true, linkTitle = true, urlTimestamp = null }) {
|
function VideoJSPlayer({ videoId = 'default-video' }) {
|
||||||
const videoRef = useRef(null);
|
const videoRef = useRef(null);
|
||||||
const playerRef = useRef(null); // Track the player instance
|
const playerRef = useRef(null); // Track the player instance
|
||||||
const userPreferences = useRef(new UserPreferences()); // User preferences instance
|
const userPreferences = useRef(new UserPreferences()); // User preferences instance
|
||||||
@ -178,17 +177,25 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
const keyboardHandler = useRef(null); // Keyboard handler instance
|
const keyboardHandler = useRef(null); // Keyboard handler instance
|
||||||
const playbackEventHandler = useRef(null); // Playback event handler instance
|
const playbackEventHandler = useRef(null); // Playback event handler instance
|
||||||
|
|
||||||
// Context menu state
|
|
||||||
const [contextMenuVisible, setContextMenuVisible] = useState(false);
|
|
||||||
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
|
|
||||||
|
|
||||||
// Check if this is an embed player (disable next video and autoplay features)
|
// Check if this is an embed player (disable next video and autoplay features)
|
||||||
const isEmbedPlayer = videoId === 'video-embed';
|
const isEmbedPlayer = videoId === 'video-embed';
|
||||||
|
|
||||||
|
// Utility function to detect touch devices
|
||||||
|
const isTouchDevice = useMemo(() => {
|
||||||
|
return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Utility function to detect iOS devices
|
||||||
|
const isIOS = useMemo(() => {
|
||||||
|
return (
|
||||||
|
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
||||||
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Environment-based development mode configuration
|
// Environment-based development mode configuration
|
||||||
const isDevMode = import.meta.env.VITE_DEV_MODE === 'true' || window.location.hostname.includes('vercel.app');
|
const isDevMode = import.meta.env.VITE_DEV_MODE === 'true' || window.location.hostname.includes('vercel.app');
|
||||||
|
// Safely access window.MEDIA_DATA with fallback using useMemo
|
||||||
// Read options from window.MEDIA_DATA if available (for consistency with embed logic)
|
|
||||||
const mediaData = useMemo(
|
const mediaData = useMemo(
|
||||||
() =>
|
() =>
|
||||||
typeof window !== 'undefined' && window.MEDIA_DATA
|
typeof window !== 'undefined' && window.MEDIA_DATA
|
||||||
@ -207,37 +214,12 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
},
|
},
|
||||||
siteUrl: 'https://deic.mediacms.io',
|
siteUrl: 'https://deic.mediacms.io',
|
||||||
nextLink: 'https://deic.mediacms.io/view?m=elygiagorgechania',
|
nextLink: 'https://deic.mediacms.io/view?m=elygiagorgechania',
|
||||||
|
urlAutoplay: true,
|
||||||
|
urlMuted: false,
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Helper to get effective value (prop or MEDIA_DATA or default)
|
|
||||||
const getOption = (propKey, mediaDataKey, defaultValue) => {
|
|
||||||
if (isEmbedPlayer) {
|
|
||||||
if (mediaData[mediaDataKey] !== undefined) return mediaData[mediaDataKey];
|
|
||||||
}
|
|
||||||
return propKey !== undefined ? propKey : defaultValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
const finalShowTitle = getOption(showTitle, 'showTitle', true);
|
|
||||||
const finalShowRelated = getOption(showRelated, 'showRelated', true);
|
|
||||||
const finalShowUserAvatar = getOption(showUserAvatar, 'showUserAvatar', true);
|
|
||||||
const finalLinkTitle = getOption(linkTitle, 'linkTitle', true);
|
|
||||||
const finalTimestamp = getOption(urlTimestamp, 'urlTimestamp', null);
|
|
||||||
|
|
||||||
// Utility function to detect touch devices
|
|
||||||
const isTouchDevice = useMemo(() => {
|
|
||||||
return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Utility function to detect iOS devices
|
|
||||||
const isIOS = useMemo(() => {
|
|
||||||
return (
|
|
||||||
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
||||||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Define chapters as JSON object
|
// Define chapters as JSON object
|
||||||
// Note: The sample-chapters.vtt file is no longer needed as chapters are now loaded from this JSON
|
// Note: The sample-chapters.vtt file is no longer needed as chapters are now loaded from this JSON
|
||||||
// CONDITIONAL LOGIC:
|
// CONDITIONAL LOGIC:
|
||||||
@ -549,6 +531,8 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
isPlayList: mediaData?.isPlayList,
|
isPlayList: mediaData?.isPlayList,
|
||||||
related_media: mediaData.data?.related_media || [],
|
related_media: mediaData.data?.related_media || [],
|
||||||
nextLink: mediaData?.nextLink || null,
|
nextLink: mediaData?.nextLink || null,
|
||||||
|
urlAutoplay: mediaData?.urlAutoplay || true,
|
||||||
|
urlMuted: mediaData?.urlMuted || false,
|
||||||
sources: getVideoSources(),
|
sources: getVideoSources(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -754,212 +738,6 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Context menu handlers
|
|
||||||
const handleContextMenu = useCallback((e) => {
|
|
||||||
// Only handle if clicking on video player area
|
|
||||||
const target = e.target;
|
|
||||||
const isVideoPlayerArea =
|
|
||||||
target.closest('.video-js') ||
|
|
||||||
target.classList.contains('vjs-tech') ||
|
|
||||||
target.tagName === 'VIDEO' ||
|
|
||||||
target.closest('video');
|
|
||||||
|
|
||||||
if (isVideoPlayerArea) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
setContextMenuPosition({ x: e.clientX, y: e.clientY });
|
|
||||||
setContextMenuVisible(true);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const closeContextMenu = () => {
|
|
||||||
setContextMenuVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to get media ID
|
|
||||||
const getMediaId = () => {
|
|
||||||
if (typeof window !== 'undefined' && window.MEDIA_DATA?.data?.friendly_token) {
|
|
||||||
return window.MEDIA_DATA.data.friendly_token;
|
|
||||||
}
|
|
||||||
if (mediaData?.data?.friendly_token) {
|
|
||||||
return mediaData.data.friendly_token;
|
|
||||||
}
|
|
||||||
// Try to get from URL (works for both main page and embed page)
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const mediaIdFromUrl = urlParams.get('m');
|
|
||||||
if (mediaIdFromUrl) {
|
|
||||||
return mediaIdFromUrl;
|
|
||||||
}
|
|
||||||
// Also check if we're on an embed page with media ID in path
|
|
||||||
const pathMatch = window.location.pathname.match(/\/embed\/([^/?]+)/);
|
|
||||||
if (pathMatch) {
|
|
||||||
return pathMatch[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentVideo.id || 'default-video';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to get base origin URL (handles embed mode)
|
|
||||||
const getBaseOrigin = () => {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
// In embed mode, try to get origin from parent window if possible
|
|
||||||
// Otherwise use current window origin
|
|
||||||
try {
|
|
||||||
// Check if we're in an iframe and can access parent
|
|
||||||
if (window.parent !== window && window.parent.location.origin) {
|
|
||||||
return window.parent.location.origin;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Cross-origin iframe, use current origin
|
|
||||||
}
|
|
||||||
return window.location.origin;
|
|
||||||
}
|
|
||||||
return mediaData.siteUrl || 'https://deic.mediacms.io';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to get embed URL
|
|
||||||
const getEmbedUrl = () => {
|
|
||||||
const mediaId = getMediaId();
|
|
||||||
const origin = getBaseOrigin();
|
|
||||||
|
|
||||||
// Try to get embed URL from config or construct it
|
|
||||||
if (typeof window !== 'undefined' && window.MediaCMS?.config?.url?.embed) {
|
|
||||||
return window.MediaCMS.config.url.embed + mediaId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: construct embed URL (check if current URL is embed format)
|
|
||||||
if (typeof window !== 'undefined' && window.location.pathname.includes('/embed')) {
|
|
||||||
// If we're already on an embed page, use current URL format
|
|
||||||
const currentUrl = new URL(window.location.href);
|
|
||||||
currentUrl.searchParams.set('m', mediaId);
|
|
||||||
return currentUrl.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default embed URL format
|
|
||||||
return `${origin}/embed?m=${mediaId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Copy video URL to clipboard
|
|
||||||
const handleCopyVideoUrl = async () => {
|
|
||||||
const mediaId = getMediaId();
|
|
||||||
const origin = getBaseOrigin();
|
|
||||||
const videoUrl = `${origin}/view?m=${mediaId}`;
|
|
||||||
|
|
||||||
// Show copy icon
|
|
||||||
if (customComponents.current?.seekIndicator) {
|
|
||||||
customComponents.current.seekIndicator.show('copy-url');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(videoUrl);
|
|
||||||
closeContextMenu();
|
|
||||||
// You can add a notification here if needed
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to copy video URL:', err);
|
|
||||||
// Fallback for older browsers
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
textArea.value = videoUrl;
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
closeContextMenu();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Copy video URL at current time to clipboard
|
|
||||||
const handleCopyVideoUrlAtTime = async () => {
|
|
||||||
if (!playerRef.current) {
|
|
||||||
closeContextMenu();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentTime = Math.floor(playerRef.current.currentTime() || 0);
|
|
||||||
const mediaId = getMediaId();
|
|
||||||
const origin = getBaseOrigin();
|
|
||||||
const videoUrl = `${origin}/view?m=${mediaId}&t=${currentTime}`;
|
|
||||||
|
|
||||||
// Show copy icon
|
|
||||||
if (customComponents.current?.seekIndicator) {
|
|
||||||
customComponents.current.seekIndicator.show('copy-url');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(videoUrl);
|
|
||||||
closeContextMenu();
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to copy video URL at time:', err);
|
|
||||||
// Fallback for older browsers
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
textArea.value = videoUrl;
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
closeContextMenu();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Copy embed code to clipboard
|
|
||||||
const handleCopyEmbedCode = async () => {
|
|
||||||
const embedUrl = getEmbedUrl();
|
|
||||||
const embedCode = `<iframe width="560" height="315" src="${embedUrl}" frameborder="0" allowfullscreen></iframe>`;
|
|
||||||
|
|
||||||
// Show copy embed icon
|
|
||||||
if (customComponents.current?.seekIndicator) {
|
|
||||||
customComponents.current.seekIndicator.show('copy-embed');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(embedCode);
|
|
||||||
closeContextMenu();
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to copy embed code:', err);
|
|
||||||
// Fallback for older browsers
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
textArea.value = embedCode;
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
closeContextMenu();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add context menu handler directly to video element and document (works before and after Video.js initialization)
|
|
||||||
useEffect(() => {
|
|
||||||
const videoElement = videoRef.current;
|
|
||||||
|
|
||||||
// Attach to document with capture to catch all contextmenu events, then filter
|
|
||||||
const documentHandler = (e) => {
|
|
||||||
// Check if the event originated from within the video player
|
|
||||||
const target = e.target;
|
|
||||||
const playerWrapper =
|
|
||||||
videoElement?.closest('.video-js') || document.querySelector(`#${videoId}`)?.closest('.video-js');
|
|
||||||
|
|
||||||
if (playerWrapper && (playerWrapper.contains(target) || target === playerWrapper)) {
|
|
||||||
handleContextMenu(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use capture phase on document to catch before anything else
|
|
||||||
document.addEventListener('contextmenu', documentHandler, true);
|
|
||||||
|
|
||||||
// Also attach directly to video element
|
|
||||||
if (videoElement) {
|
|
||||||
videoElement.addEventListener('contextmenu', handleContextMenu, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('contextmenu', documentHandler, true);
|
|
||||||
if (videoElement) {
|
|
||||||
videoElement.removeEventListener('contextmenu', handleContextMenu, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [handleContextMenu, videoId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only initialize if we don't already have a player and element exists
|
// Only initialize if we don't already have a player and element exists
|
||||||
if (videoRef.current && !playerRef.current) {
|
if (videoRef.current && !playerRef.current) {
|
||||||
@ -1300,9 +1078,6 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
currentVideo,
|
currentVideo,
|
||||||
relatedVideos,
|
relatedVideos,
|
||||||
goToNextVideo,
|
goToNextVideo,
|
||||||
showRelated: finalShowRelated,
|
|
||||||
showUserAvatar: finalShowUserAvatar,
|
|
||||||
linkTitle: finalLinkTitle,
|
|
||||||
});
|
});
|
||||||
customComponents.current.endScreenHandler = endScreenHandler; // Store for cleanup
|
customComponents.current.endScreenHandler = endScreenHandler; // Store for cleanup
|
||||||
|
|
||||||
@ -1323,8 +1098,8 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle URL timestamp parameter
|
// Handle URL timestamp parameter
|
||||||
if (finalTimestamp !== null && finalTimestamp >= 0) {
|
if (mediaData.urlTimestamp !== null && mediaData.urlTimestamp >= 0) {
|
||||||
const timestamp = finalTimestamp;
|
const timestamp = mediaData.urlTimestamp;
|
||||||
|
|
||||||
// Wait for video metadata to be loaded before seeking
|
// Wait for video metadata to be loaded before seeking
|
||||||
if (playerRef.current.readyState() >= 1) {
|
if (playerRef.current.readyState() >= 1) {
|
||||||
@ -2222,10 +1997,6 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
authorThumbnail: currentVideo.author_thumbnail,
|
authorThumbnail: currentVideo.author_thumbnail,
|
||||||
videoTitle: currentVideo.title,
|
videoTitle: currentVideo.title,
|
||||||
videoUrl: currentVideo.url,
|
videoUrl: currentVideo.url,
|
||||||
showTitle: finalShowTitle,
|
|
||||||
showRelated: finalShowRelated,
|
|
||||||
showUserAvatar: finalShowUserAvatar,
|
|
||||||
linkTitle: finalLinkTitle,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// END: Add Embed Info Overlay Component
|
// END: Add Embed Info Overlay Component
|
||||||
@ -2312,72 +2083,20 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
// Make the video element focusable
|
// Make the video element focusable
|
||||||
const videoElement = playerRef.current.el();
|
const videoElement = playerRef.current.el();
|
||||||
videoElement.setAttribute('tabindex', '0');
|
videoElement.setAttribute('tabindex', '0');
|
||||||
|
|
||||||
if (!isEmbedPlayer) {
|
|
||||||
videoElement.focus();
|
videoElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add context menu (right-click) handler to the player wrapper and video element
|
|
||||||
// Attach to player wrapper (this catches all clicks on the player)
|
|
||||||
videoElement.addEventListener('contextmenu', handleContextMenu, true);
|
|
||||||
|
|
||||||
// Also try to attach to the actual video tech element
|
|
||||||
const attachContextMenu = () => {
|
|
||||||
const techElement =
|
|
||||||
playerRef.current.el().querySelector('.vjs-tech') ||
|
|
||||||
playerRef.current.el().querySelector('video') ||
|
|
||||||
(playerRef.current.tech() && playerRef.current.tech().el());
|
|
||||||
|
|
||||||
if (techElement && techElement !== videoRef.current && techElement !== videoElement) {
|
|
||||||
// Use capture phase to catch before Video.js might prevent it
|
|
||||||
techElement.addEventListener('contextmenu', handleContextMenu, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Try to attach immediately
|
|
||||||
attachContextMenu();
|
|
||||||
|
|
||||||
// Also try after a short delay in case elements aren't ready yet
|
|
||||||
setTimeout(() => {
|
|
||||||
attachContextMenu();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
// Also try when video is loaded
|
|
||||||
playerRef.current.one('loadedmetadata', () => {
|
|
||||||
attachContextMenu();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//}, 0);
|
//}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup: Remove context menu event listener
|
|
||||||
return () => {
|
|
||||||
if (playerRef.current && playerRef.current.el()) {
|
|
||||||
const playerEl = playerRef.current.el();
|
|
||||||
playerEl.removeEventListener('contextmenu', handleContextMenu, true);
|
|
||||||
|
|
||||||
const techElement =
|
|
||||||
playerEl.querySelector('.vjs-tech') ||
|
|
||||||
playerEl.querySelector('video') ||
|
|
||||||
(playerRef.current.tech() && playerRef.current.tech().el());
|
|
||||||
if (techElement) {
|
|
||||||
techElement.removeEventListener('contextmenu', handleContextMenu, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
id={videoId}
|
id={videoId}
|
||||||
controls={true}
|
controls={true}
|
||||||
className={`video-js ${isEmbedPlayer ? 'vjs-fill' : 'vjs-fluid'} vjs-default-skin${currentVideo.useRoundedCorners ? ' video-js-rounded-corners' : ''}`}
|
className={`video-js vjs-fluid vjs-default-skin${currentVideo.useRoundedCorners ? ' video-js-rounded-corners' : ''}`}
|
||||||
preload="auto"
|
preload="auto"
|
||||||
poster={currentVideo.poster}
|
poster={currentVideo.poster}
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
@ -2410,15 +2129,6 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
|
|||||||
chaptersData.length > 0 &&
|
chaptersData.length > 0 &&
|
||||||
(console.log('chaptersData', chaptersData), (<track kind="chapters" src="/sample-chapters.vtt" />))} */}
|
(console.log('chaptersData', chaptersData), (<track kind="chapters" src="/sample-chapters.vtt" />))} */}
|
||||||
</video>
|
</video>
|
||||||
<VideoContextMenu
|
|
||||||
visible={contextMenuVisible}
|
|
||||||
position={contextMenuPosition}
|
|
||||||
onClose={closeContextMenu}
|
|
||||||
onCopyVideoUrl={handleCopyVideoUrl}
|
|
||||||
onCopyVideoUrlAtTime={handleCopyVideoUrlAtTime}
|
|
||||||
onCopyEmbedCode={handleCopyEmbedCode}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,17 +63,7 @@ export class EndScreenHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleVideoEnded() {
|
handleVideoEnded() {
|
||||||
const {
|
const { isEmbedPlayer, userPreferences, mediaData, currentVideo, relatedVideos, goToNextVideo } = this.options;
|
||||||
isEmbedPlayer,
|
|
||||||
userPreferences,
|
|
||||||
mediaData,
|
|
||||||
currentVideo,
|
|
||||||
relatedVideos,
|
|
||||||
goToNextVideo,
|
|
||||||
showRelated,
|
|
||||||
showUserAvatar,
|
|
||||||
linkTitle,
|
|
||||||
} = this.options;
|
|
||||||
|
|
||||||
// For embed players, show big play button when video ends
|
// For embed players, show big play button when video ends
|
||||||
if (isEmbedPlayer) {
|
if (isEmbedPlayer) {
|
||||||
@ -83,34 +73,6 @@ export class EndScreenHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If showRelated is false, we don't show the end screen or autoplay countdown
|
|
||||||
if (showRelated === false) {
|
|
||||||
// But we still want to keep the control bar visible and hide the poster
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.player && !this.player.isDisposed()) {
|
|
||||||
const playerEl = this.player.el();
|
|
||||||
if (playerEl) {
|
|
||||||
// Hide poster elements
|
|
||||||
const posterElements = playerEl.querySelectorAll('.vjs-poster');
|
|
||||||
posterElements.forEach((posterEl) => {
|
|
||||||
posterEl.style.display = 'none';
|
|
||||||
posterEl.style.visibility = 'hidden';
|
|
||||||
posterEl.style.opacity = '0';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keep control bar visible
|
|
||||||
const controlBar = this.player.getChild('controlBar');
|
|
||||||
if (controlBar) {
|
|
||||||
controlBar.show();
|
|
||||||
controlBar.el().style.opacity = '1';
|
|
||||||
controlBar.el().style.pointerEvents = 'auto';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep controls active after video ends
|
// Keep controls active after video ends
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.player && !this.player.isDisposed()) {
|
if (this.player && !this.player.isDisposed()) {
|
||||||
|
|||||||
@ -31,11 +31,8 @@ const VideoJSEmbed = ({
|
|||||||
poster,
|
poster,
|
||||||
previewSprite,
|
previewSprite,
|
||||||
subtitlesInfo,
|
subtitlesInfo,
|
||||||
|
enableAutoplay,
|
||||||
inEmbed,
|
inEmbed,
|
||||||
showTitle,
|
|
||||||
showRelated,
|
|
||||||
showUserAvatar,
|
|
||||||
linkTitle,
|
|
||||||
hasTheaterMode,
|
hasTheaterMode,
|
||||||
hasNextLink,
|
hasNextLink,
|
||||||
nextLink,
|
nextLink,
|
||||||
@ -65,10 +62,8 @@ const VideoJSEmbed = ({
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
// Get URL parameters for autoplay, muted, and timestamp
|
// Get URL parameters for autoplay, muted, and timestamp
|
||||||
const urlTimestamp = getUrlParameter('t');
|
const urlTimestamp = getUrlParameter('t');
|
||||||
|
const urlAutoplay = getUrlParameter('autoplay');
|
||||||
const urlMuted = getUrlParameter('muted');
|
const urlMuted = getUrlParameter('muted');
|
||||||
const urlShowRelated = getUrlParameter('showRelated');
|
|
||||||
const urlShowUserAvatar = getUrlParameter('showUserAvatar');
|
|
||||||
const urlLinkTitle = getUrlParameter('linkTitle');
|
|
||||||
|
|
||||||
window.MEDIA_DATA = {
|
window.MEDIA_DATA = {
|
||||||
data: data || {},
|
data: data || {},
|
||||||
@ -76,7 +71,7 @@ const VideoJSEmbed = ({
|
|||||||
version: version,
|
version: version,
|
||||||
isPlayList: isPlayList,
|
isPlayList: isPlayList,
|
||||||
playerVolume: playerVolume || 0.5,
|
playerVolume: playerVolume || 0.5,
|
||||||
playerSoundMuted: urlMuted === '1',
|
playerSoundMuted: playerSoundMuted || (urlMuted === '1'),
|
||||||
videoQuality: videoQuality || 'auto',
|
videoQuality: videoQuality || 'auto',
|
||||||
videoPlaybackSpeed: videoPlaybackSpeed || 1,
|
videoPlaybackSpeed: videoPlaybackSpeed || 1,
|
||||||
inTheaterMode: inTheaterMode || false,
|
inTheaterMode: inTheaterMode || false,
|
||||||
@ -88,11 +83,8 @@ const VideoJSEmbed = ({
|
|||||||
poster: poster || '',
|
poster: poster || '',
|
||||||
previewSprite: previewSprite || null,
|
previewSprite: previewSprite || null,
|
||||||
subtitlesInfo: subtitlesInfo || [],
|
subtitlesInfo: subtitlesInfo || [],
|
||||||
|
enableAutoplay: enableAutoplay || (urlAutoplay === '1'),
|
||||||
inEmbed: inEmbed || false,
|
inEmbed: inEmbed || false,
|
||||||
showTitle: showTitle || false,
|
|
||||||
showRelated: showRelated !== undefined ? showRelated : (urlShowRelated === '1' || urlShowRelated === 'true' || urlShowRelated === null),
|
|
||||||
showUserAvatar: showUserAvatar !== undefined ? showUserAvatar : (urlShowUserAvatar === '1' || urlShowUserAvatar === 'true' || urlShowUserAvatar === null),
|
|
||||||
linkTitle: linkTitle !== undefined ? linkTitle : (urlLinkTitle === '1' || urlLinkTitle === 'true' || urlLinkTitle === null),
|
|
||||||
hasTheaterMode: hasTheaterMode || false,
|
hasTheaterMode: hasTheaterMode || false,
|
||||||
hasNextLink: hasNextLink || false,
|
hasNextLink: hasNextLink || false,
|
||||||
nextLink: nextLink || null,
|
nextLink: nextLink || null,
|
||||||
@ -100,10 +92,8 @@ const VideoJSEmbed = ({
|
|||||||
errorMessage: errorMessage || '',
|
errorMessage: errorMessage || '',
|
||||||
// URL parameters
|
// URL parameters
|
||||||
urlTimestamp: urlTimestamp ? parseInt(urlTimestamp, 10) : null,
|
urlTimestamp: urlTimestamp ? parseInt(urlTimestamp, 10) : null,
|
||||||
|
urlAutoplay: urlAutoplay === '1',
|
||||||
urlMuted: urlMuted === '1',
|
urlMuted: urlMuted === '1',
|
||||||
urlShowRelated: urlShowRelated === '1' || urlShowRelated === 'true',
|
|
||||||
urlShowUserAvatar: urlShowUserAvatar === '1' || urlShowUserAvatar === 'true',
|
|
||||||
urlLinkTitle: urlLinkTitle === '1' || urlLinkTitle === 'true',
|
|
||||||
onClickNextCallback: onClickNextCallback || null,
|
onClickNextCallback: onClickNextCallback || null,
|
||||||
onClickPreviousCallback: onClickPreviousCallback || null,
|
onClickPreviousCallback: onClickPreviousCallback || null,
|
||||||
onStateUpdateCallback: onStateUpdateCallback || null,
|
onStateUpdateCallback: onStateUpdateCallback || null,
|
||||||
@ -186,18 +176,12 @@ const VideoJSEmbed = ({
|
|||||||
// Scroll to the video player with smooth behavior
|
// Scroll to the video player with smooth behavior
|
||||||
const videoElement = document.querySelector(inEmbedRef.current ? '#video-embed' : '#video-main');
|
const videoElement = document.querySelector(inEmbedRef.current ? '#video-embed' : '#video-main');
|
||||||
if (videoElement) {
|
if (videoElement) {
|
||||||
const urlScroll = getUrlParameter('scroll');
|
|
||||||
const isIframe = window.parent !== window;
|
|
||||||
|
|
||||||
// Only scroll if not in an iframe, OR if explicitly requested via scroll=1 parameter
|
|
||||||
if (!isIframe || urlScroll === '1' || urlScroll === 'true') {
|
|
||||||
videoElement.scrollIntoView({
|
videoElement.scrollIntoView({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
block: 'center',
|
block: 'center',
|
||||||
inline: 'nearest'
|
inline: 'nearest'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('VideoJS player not found for timestamp navigation');
|
console.warn('VideoJS player not found for timestamp navigation');
|
||||||
}
|
}
|
||||||
@ -236,14 +220,7 @@ const VideoJSEmbed = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="video-js-wrapper" ref={containerRef}>
|
<div className="video-js-wrapper" ref={containerRef}>
|
||||||
{inEmbed ? (
|
{inEmbed ? <div id="video-js-root-embed" className="video-js-root-embed" /> : <div id="video-js-root-main" className="video-js-root-main" />}
|
||||||
<div
|
|
||||||
id="video-js-root-embed"
|
|
||||||
className="video-js-root-embed"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div id="video-js-root-main" className="video-js-root-main" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,32 +4,10 @@ import { LinksContext, SiteConsumer } from '../../utils/contexts/';
|
|||||||
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
||||||
import { PageActions, MediaPageActions } from '../../utils/actions/';
|
import { PageActions, MediaPageActions } from '../../utils/actions/';
|
||||||
import { CircleIconButton, MaterialIcon, NumericInputWithUnit } from '../_shared/';
|
import { CircleIconButton, MaterialIcon, NumericInputWithUnit } from '../_shared/';
|
||||||
|
import VideoViewer from '../media-viewer/VideoViewer';
|
||||||
const EMBED_OPTIONS_STORAGE_KEY = 'mediacms_embed_options';
|
|
||||||
|
|
||||||
function loadEmbedOptions() {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem(EMBED_OPTIONS_STORAGE_KEY);
|
|
||||||
if (saved) {
|
|
||||||
return JSON.parse(saved);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore localStorage errors
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveEmbedOptions(options) {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(EMBED_OPTIONS_STORAGE_KEY, JSON.stringify(options));
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore localStorage errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MediaShareEmbed(props) {
|
export function MediaShareEmbed(props) {
|
||||||
const embedVideoDimensions = PageStore.get('config-options').embedded.video.dimensions;
|
const embedVideoDimensions = PageStore.get('config-options').embedded.video.dimensions;
|
||||||
const savedOptions = loadEmbedOptions();
|
|
||||||
|
|
||||||
const links = useContext(LinksContext);
|
const links = useContext(LinksContext);
|
||||||
|
|
||||||
@ -40,19 +18,12 @@ export function MediaShareEmbed(props) {
|
|||||||
const onRightBottomRef = useRef(null);
|
const onRightBottomRef = useRef(null);
|
||||||
|
|
||||||
const [maxHeight, setMaxHeight] = useState(window.innerHeight - 144 + 56);
|
const [maxHeight, setMaxHeight] = useState(window.innerHeight - 144 + 56);
|
||||||
const [keepAspectRatio, setKeepAspectRatio] = useState(savedOptions?.keepAspectRatio ?? true);
|
const [keepAspectRatio, setKeepAspectRatio] = useState(false);
|
||||||
const [showTitle, setShowTitle] = useState(savedOptions?.showTitle ?? true);
|
const [aspectRatio, setAspectRatio] = useState('16:9');
|
||||||
const [showRelated, setShowRelated] = useState(savedOptions?.showRelated ?? true);
|
const [embedWidthValue, setEmbedWidthValue] = useState(embedVideoDimensions.width);
|
||||||
const [showUserAvatar, setShowUserAvatar] = useState(savedOptions?.showUserAvatar ?? true);
|
const [embedWidthUnit, setEmbedWidthUnit] = useState(embedVideoDimensions.widthUnit);
|
||||||
const [linkTitle, setLinkTitle] = useState(savedOptions?.linkTitle ?? true);
|
const [embedHeightValue, setEmbedHeightValue] = useState(embedVideoDimensions.height);
|
||||||
const [responsive, setResponsive] = useState(savedOptions?.responsive ?? false);
|
const [embedHeightUnit, setEmbedHeightUnit] = useState(embedVideoDimensions.heightUnit);
|
||||||
const [startAt, setStartAt] = useState(false);
|
|
||||||
const [startTime, setStartTime] = useState('0:00');
|
|
||||||
const [aspectRatio, setAspectRatio] = useState(savedOptions?.aspectRatio ?? '16:9');
|
|
||||||
const [embedWidthValue, setEmbedWidthValue] = useState(savedOptions?.embedWidthValue ?? embedVideoDimensions.width);
|
|
||||||
const [embedWidthUnit, setEmbedWidthUnit] = useState(savedOptions?.embedWidthUnit ?? embedVideoDimensions.widthUnit);
|
|
||||||
const [embedHeightValue, setEmbedHeightValue] = useState(savedOptions?.embedHeightValue ?? embedVideoDimensions.height);
|
|
||||||
const [embedHeightUnit, setEmbedHeightUnit] = useState(savedOptions?.embedHeightUnit ?? embedVideoDimensions.heightUnit);
|
|
||||||
const [rightMiddlePositionTop, setRightMiddlePositionTop] = useState(60);
|
const [rightMiddlePositionTop, setRightMiddlePositionTop] = useState(60);
|
||||||
const [rightMiddlePositionBottom, setRightMiddlePositionBottom] = useState(60);
|
const [rightMiddlePositionBottom, setRightMiddlePositionBottom] = useState(60);
|
||||||
const [unitOptions, setUnitOptions] = useState([
|
const [unitOptions, setUnitOptions] = useState([
|
||||||
@ -100,65 +71,36 @@ export function MediaShareEmbed(props) {
|
|||||||
setEmbedHeightUnit(newVal);
|
setEmbedHeightUnit(newVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowTitleChange() {
|
function onKeepAspectRatioChange() {
|
||||||
setShowTitle(!showTitle);
|
const newVal = !keepAspectRatio;
|
||||||
}
|
|
||||||
|
|
||||||
function onShowRelatedChange() {
|
|
||||||
setShowRelated(!showRelated);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onShowUserAvatarChange() {
|
|
||||||
setShowUserAvatar(!showUserAvatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLinkTitleChange() {
|
|
||||||
setLinkTitle(!linkTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onResponsiveChange() {
|
|
||||||
const nextResponsive = !responsive;
|
|
||||||
setResponsive(nextResponsive);
|
|
||||||
|
|
||||||
if (!nextResponsive) {
|
|
||||||
if (aspectRatio !== 'custom') {
|
|
||||||
const arr = aspectRatio.split(':');
|
const arr = aspectRatio.split(':');
|
||||||
const x = arr[0];
|
const x = arr[0];
|
||||||
const y = arr[1];
|
const y = arr[1];
|
||||||
|
|
||||||
setKeepAspectRatio(true);
|
setKeepAspectRatio(newVal);
|
||||||
setEmbedHeightValue(parseInt((embedWidthValue * y) / x, 10));
|
setEmbedWidthUnit(newVal ? 'px' : embedWidthUnit);
|
||||||
} else {
|
setEmbedHeightUnit(newVal ? 'px' : embedHeightUnit);
|
||||||
setKeepAspectRatio(false);
|
setEmbedHeightValue(newVal ? parseInt((embedWidthValue * y) / x, 10) : embedHeightValue);
|
||||||
}
|
setUnitOptions(
|
||||||
} else {
|
newVal
|
||||||
setKeepAspectRatio(false);
|
? [{ key: 'px', label: 'px' }]
|
||||||
}
|
: [
|
||||||
}
|
{ key: 'px', label: 'px' },
|
||||||
|
{ key: 'percent', label: '%' },
|
||||||
function onStartAtChange() {
|
]
|
||||||
setStartAt(!startAt);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
function onStartTimeChange(e) {
|
|
||||||
setStartTime(e.target.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAspectRatioChange() {
|
function onAspectRatioChange() {
|
||||||
const newVal = aspectRatioValueRef.current.value;
|
const newVal = aspectRatioValueRef.current.value;
|
||||||
|
|
||||||
if (newVal === 'custom') {
|
|
||||||
setAspectRatio(newVal);
|
|
||||||
setKeepAspectRatio(false);
|
|
||||||
} else {
|
|
||||||
const arr = newVal.split(':');
|
const arr = newVal.split(':');
|
||||||
const x = arr[0];
|
const x = arr[0];
|
||||||
const y = arr[1];
|
const y = arr[1];
|
||||||
|
|
||||||
setAspectRatio(newVal);
|
setAspectRatio(newVal);
|
||||||
setKeepAspectRatio(true);
|
setEmbedHeightValue(keepAspectRatio ? parseInt((embedWidthValue * y) / x, 10) : embedHeightValue);
|
||||||
setEmbedHeightValue(parseInt((embedWidthValue * y) / x, 10));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWindowResize() {
|
function onWindowResize() {
|
||||||
@ -188,88 +130,13 @@ export function MediaShareEmbed(props) {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Save embed options to localStorage when they change (except startAt/startTime)
|
|
||||||
useEffect(() => {
|
|
||||||
saveEmbedOptions({
|
|
||||||
showTitle,
|
|
||||||
showRelated,
|
|
||||||
showUserAvatar,
|
|
||||||
linkTitle,
|
|
||||||
responsive,
|
|
||||||
aspectRatio,
|
|
||||||
embedWidthValue,
|
|
||||||
embedWidthUnit,
|
|
||||||
embedHeightValue,
|
|
||||||
embedHeightUnit,
|
|
||||||
keepAspectRatio,
|
|
||||||
});
|
|
||||||
}, [showTitle, showRelated, showUserAvatar, linkTitle, responsive, aspectRatio, embedWidthValue, embedWidthUnit, embedHeightValue, embedHeightUnit, keepAspectRatio]);
|
|
||||||
|
|
||||||
function getEmbedCode() {
|
|
||||||
const mediaId = MediaPageStore.get('media-id');
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
if (showTitle) params.set('showTitle', '1');
|
|
||||||
else params.set('showTitle', '0');
|
|
||||||
|
|
||||||
if (showRelated) params.set('showRelated', '1');
|
|
||||||
else params.set('showRelated', '0');
|
|
||||||
|
|
||||||
if (showUserAvatar) params.set('showUserAvatar', '1');
|
|
||||||
else params.set('showUserAvatar', '0');
|
|
||||||
|
|
||||||
if (linkTitle) params.set('linkTitle', '1');
|
|
||||||
else params.set('linkTitle', '0');
|
|
||||||
|
|
||||||
if (startAt && startTime) {
|
|
||||||
const parts = startTime.split(':').reverse();
|
|
||||||
let seconds = 0;
|
|
||||||
if (parts[0]) seconds += parseInt(parts[0], 10) || 0;
|
|
||||||
if (parts[1]) seconds += (parseInt(parts[1], 10) || 0) * 60;
|
|
||||||
if (parts[2]) seconds += (parseInt(parts[2], 10) || 0) * 3600;
|
|
||||||
if (seconds > 0) params.set('t', seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
const separator = links.embed.includes('?') ? '&' : '?';
|
|
||||||
const finalUrl = `${links.embed}${mediaId}${separator}${params.toString()}`;
|
|
||||||
|
|
||||||
if (responsive) {
|
|
||||||
if (aspectRatio === 'custom') {
|
|
||||||
// Use current width/height values to calculate aspect ratio for custom
|
|
||||||
const ratio = `${embedWidthValue} / ${embedHeightValue}`;
|
|
||||||
const maxWidth = `calc(100vh * ${embedWidthValue} / ${embedHeightValue})`;
|
|
||||||
return `<iframe src="${finalUrl}" style="width:100%;max-width:${maxWidth};aspect-ratio:${ratio};display:block;margin:auto;border:0;" allowFullScreen></iframe>`;
|
|
||||||
}
|
|
||||||
const arr = aspectRatio.split(':');
|
|
||||||
const ratio = `${arr[0]} / ${arr[1]}`;
|
|
||||||
const maxWidth = `calc(100vh * ${arr[0]} / ${arr[1]})`;
|
|
||||||
return `<iframe src="${finalUrl}" style="width:100%;max-width:${maxWidth};aspect-ratio:${ratio};display:block;margin:auto;border:0;" allowFullScreen></iframe>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = 'percent' === embedWidthUnit ? embedWidthValue + '%' : embedWidthValue;
|
|
||||||
const height = 'percent' === embedHeightUnit ? embedHeightValue + '%' : embedHeightValue;
|
|
||||||
return `<iframe width="${width}" height="${height}" src="${finalUrl}" frameBorder="0" allowFullScreen></iframe>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="share-embed" style={{ maxHeight: maxHeight + 'px' }}>
|
<div className="share-embed" style={{ maxHeight: maxHeight + 'px' }}>
|
||||||
<div className="share-embed-inner">
|
<div className="share-embed-inner">
|
||||||
<div className="on-left">
|
<div className="on-left">
|
||||||
<div className="media-embed-wrap">
|
<div className="media-embed-wrap">
|
||||||
<SiteConsumer>
|
<SiteConsumer>
|
||||||
{(site) => {
|
{(site) => <VideoViewer data={MediaPageStore.get('media-data')} siteUrl={site.url} inEmbed={true} />}
|
||||||
const previewUrl = `${links.embed + MediaPageStore.get('media-id')}&showTitle=${showTitle ? '1' : '0'}&showRelated=${showRelated ? '1' : '0'}&showUserAvatar=${showUserAvatar ? '1' : '0'}&linkTitle=${linkTitle ? '1' : '0'}${startAt ? '&t=' + (startTime.split(':').reverse().reduce((acc, cur, i) => acc + (parseInt(cur, 10) || 0) * Math.pow(60, i), 0)) : ''}`;
|
|
||||||
|
|
||||||
const style = {};
|
|
||||||
style.width = '100%';
|
|
||||||
style.height = '480px';
|
|
||||||
style.overflow = 'hidden';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={style}>
|
|
||||||
<iframe width="100%" height="100%" src={previewUrl} frameBorder="0" allowFullScreen></iframe>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</SiteConsumer>
|
</SiteConsumer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -291,7 +158,16 @@ export function MediaShareEmbed(props) {
|
|||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
readOnly
|
readOnly
|
||||||
value={getEmbedCode()}
|
value={
|
||||||
|
'<iframe width="' +
|
||||||
|
('percent' === embedWidthUnit ? embedWidthValue + '%' : embedWidthValue) +
|
||||||
|
'" height="' +
|
||||||
|
('percent' === embedHeightUnit ? embedHeightValue + '%' : embedHeightValue) +
|
||||||
|
'" src="' +
|
||||||
|
links.embed +
|
||||||
|
MediaPageStore.get('media-id') +
|
||||||
|
'" frameborder="0" allowfullscreen></iframe>'
|
||||||
|
}
|
||||||
></textarea>
|
></textarea>
|
||||||
|
|
||||||
<div className="iframe-config">
|
<div className="iframe-config">
|
||||||
@ -303,79 +179,34 @@ export function MediaShareEmbed(props) {
|
|||||||
</div>*/}
|
</div>*/}
|
||||||
|
|
||||||
<div className="option-content">
|
<div className="option-content">
|
||||||
<div className="ratio-options" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 10px' }}>
|
<div className="ratio-options">
|
||||||
<div className="options-group">
|
<div className="options-group">
|
||||||
<label style={{ minHeight: '36px', whiteSpace: 'nowrap' }}>
|
<label style={{ minHeight: '36px' }}>
|
||||||
<input type="checkbox" checked={showTitle} onChange={onShowTitleChange} />
|
<input type="checkbox" checked={keepAspectRatio} onChange={onKeepAspectRatioChange} />
|
||||||
Show title
|
Keep aspect ratio
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!keepAspectRatio ? null : (
|
||||||
<div className="options-group">
|
<div className="options-group">
|
||||||
<label style={{ minHeight: '36px', whiteSpace: 'nowrap', opacity: showTitle ? 1 : 0.5 }}>
|
<select ref={aspectRatioValueRef} onChange={onAspectRatioChange} value={aspectRatio}>
|
||||||
<input type="checkbox" checked={linkTitle} onChange={onLinkTitleChange} disabled={!showTitle} />
|
<optgroup label="Horizontal orientation">
|
||||||
Link title
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="options-group">
|
|
||||||
<label style={{ minHeight: '36px', whiteSpace: 'nowrap' }}>
|
|
||||||
<input type="checkbox" checked={showRelated} onChange={onShowRelatedChange} />
|
|
||||||
Show related
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="options-group">
|
|
||||||
<label style={{ minHeight: '36px', whiteSpace: 'nowrap', opacity: showTitle ? 1 : 0.5 }}>
|
|
||||||
<input type="checkbox" checked={showUserAvatar} onChange={onShowUserAvatarChange} disabled={!showTitle} />
|
|
||||||
Show user avatar
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="options-group" style={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
<label style={{ minHeight: '36px', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', marginRight: '10px' }}>
|
|
||||||
<input type="checkbox" checked={responsive} onChange={onResponsiveChange} />
|
|
||||||
Responsive
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="options-group" style={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
<label style={{ minHeight: '36px', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', marginRight: '10px' }}>
|
|
||||||
<input type="checkbox" checked={startAt} onChange={onStartAtChange} />
|
|
||||||
Start at
|
|
||||||
</label>
|
|
||||||
{startAt && (
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={startTime}
|
|
||||||
onChange={onStartTimeChange}
|
|
||||||
style={{ width: '60px', height: '28px', fontSize: '12px', padding: '2px 5px' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="options-group" style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
|
||||||
<div style={{ fontSize: '12px', marginBottom: '4px', color: 'rgba(0,0,0,0.6)' }}>Aspect Ratio</div>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
<select
|
|
||||||
ref={aspectRatioValueRef}
|
|
||||||
onChange={onAspectRatioChange}
|
|
||||||
value={aspectRatio}
|
|
||||||
style={{ height: '28px', fontSize: '12px' }}
|
|
||||||
>
|
|
||||||
<option value="16:9">16:9</option>
|
<option value="16:9">16:9</option>
|
||||||
<option value="4:3">4:3</option>
|
<option value="4:3">4:3</option>
|
||||||
<option value="3:2">3:2</option>
|
<option value="3:2">3:2</option>
|
||||||
<option value="custom">Custom</option>
|
</optgroup>
|
||||||
|
<optgroup label="Vertical orientation">
|
||||||
|
<option value="9:16">9:16</option>
|
||||||
|
<option value="3:4">3:4</option>
|
||||||
|
<option value="2:3">2:3</option>
|
||||||
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
{!responsive && (
|
|
||||||
<>
|
|
||||||
<div className="options-group">
|
<div className="options-group">
|
||||||
<NumericInputWithUnit
|
<NumericInputWithUnit
|
||||||
valueCallback={onEmbedWidthValueChange}
|
valueCallback={onEmbedWidthValueChange}
|
||||||
@ -401,8 +232,6 @@ export function MediaShareEmbed(props) {
|
|||||||
units={unitOptions}
|
units={unitOptions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
@use "sass:math";
|
@use "sass:math";
|
||||||
@import '../../../css/includes/_variables.scss';
|
@import "../../../css/includes/_variables.scss";
|
||||||
@import '../../../css/includes/_variables_dimensions.scss';
|
@import "../../../css/includes/_variables_dimensions.scss";
|
||||||
|
|
||||||
.visible-sidebar .page-main-wrap {
|
.visible-sidebar .page-main-wrap {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
@ -119,7 +119,7 @@
|
|||||||
background-color: var(--media-actions-share-copy-field-bg-color);
|
background-color: var(--media-actions-share-copy-field-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'] {
|
input[type="text"] {
|
||||||
color: var(--media-actions-share-copy-field-input-text-color);
|
color: var(--media-actions-share-copy-field-input-text-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@
|
|||||||
color: var(--report-form-field-label-text-color);
|
color: var(--report-form-field-label-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'],
|
input[type="text"],
|
||||||
textarea {
|
textarea {
|
||||||
color: var(--report-form-field-input-text-color);
|
color: var(--report-form-field-input-text-color);
|
||||||
border-color: var(--report-form-field-input-border-color);
|
border-color: var(--report-form-field-input-border-color);
|
||||||
@ -479,7 +479,7 @@
|
|||||||
|
|
||||||
&.audio-player-container {
|
&.audio-player-container {
|
||||||
&:before {
|
&:before {
|
||||||
content: '\E3A1';
|
content: "\E3A1";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@ -490,12 +490,11 @@
|
|||||||
|
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: 'Material Icons';
|
font-family: "Material Icons";
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.vjs-big-play-button {
|
.vjs-big-play-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,6 +513,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.embedded-app {
|
||||||
|
.viewer-container,
|
||||||
|
.viewer-info {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.viewer-image-container {
|
.viewer-image-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
@ -550,8 +556,6 @@
|
|||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.slideshow-image img {
|
.slideshow-image img {
|
||||||
display: block;
|
display: block;
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -560,7 +564,9 @@
|
|||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
||||||
transition: transform 60s ease-in-out, opacity 60 ease-in-out;
|
transition:
|
||||||
|
transform 60s ease-in-out,
|
||||||
|
opacity 60 ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slideshow-title {
|
.slideshow-title {
|
||||||
@ -572,7 +578,6 @@
|
|||||||
z-index: 1200;
|
z-index: 1200;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.arrow {
|
.arrow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -590,7 +595,9 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
transition: background-color 0.2s ease, transform 0.2s ease;
|
transition:
|
||||||
|
background-color 0.2s ease,
|
||||||
|
transform 0.2s ease;
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,18 +692,19 @@
|
|||||||
width: 100%; // Default width for mobile
|
width: 100%; // Default width for mobile
|
||||||
height: 400px; // Default height for mobile
|
height: 400px; // Default height for mobile
|
||||||
|
|
||||||
@media (min-width: 768px) and (max-width: 1023px) { // Tablets
|
@media (min-width: 768px) and (max-width: 1023px) {
|
||||||
|
// Tablets
|
||||||
width: 90%;
|
width: 90%;
|
||||||
height: 600px;
|
height: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) { // Desktop
|
@media (min-width: 1024px) {
|
||||||
|
// Desktop
|
||||||
width: 85%;
|
width: 85%;
|
||||||
height: 900px;
|
height: 900px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.viewer-container .player-container.viewer-pdf-container,
|
.viewer-container .player-container.viewer-pdf-container,
|
||||||
.viewer-container .player-container.viewer-attachment-container {
|
.viewer-container .player-container.viewer-attachment-container {
|
||||||
background-color: var(--item-thumb-bg-color);
|
background-color: var(--item-thumb-bg-color);
|
||||||
@ -1006,7 +1014,7 @@
|
|||||||
&.like,
|
&.like,
|
||||||
&.dislike {
|
&.dislike {
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: -4px;
|
left: -4px;
|
||||||
@ -1146,7 +1154,7 @@
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'] {
|
input[type="text"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
padding: 1px 0 1px 16px;
|
padding: 1px 0 1px 16px;
|
||||||
@ -1206,13 +1214,18 @@
|
|||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
box-shadow: 0 16px 24px 2px rgba(#000, 0.14), 0 6px 30px 5px rgba(#000, 0.12),
|
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);
|
0 8px 10px -5px rgba(#000, 0.4);
|
||||||
|
|
||||||
&.main-options,
|
&.main-options,
|
||||||
&.video-download-options {
|
&.video-download-options {
|
||||||
width: 240px;
|
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);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1270,7 +1283,9 @@
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
text-align: initial;
|
text-align: initial;
|
||||||
|
|
||||||
box-shadow: rgba(#000, 0.14) 0px 16px 24px 2px, rgba(#000, 0.12) 0px 6px 30px 5px,
|
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;
|
rgba(#000, 0.4) 0px 8px 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1303,13 +1318,18 @@
|
|||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
box-shadow: 0 16px 24px 2px rgba(#000, 0.14), 0 6px 30px 5px rgba(#000, 0.12),
|
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);
|
0 8px 10px -5px rgba(#000, 0.4);
|
||||||
|
|
||||||
&.main-options,
|
&.main-options,
|
||||||
&.video-download-options {
|
&.video-download-options {
|
||||||
width: 240px;
|
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);
|
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 {
|
.popup-main {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@ -1412,7 +1432,7 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'],
|
input[type="text"],
|
||||||
textarea {
|
textarea {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -1435,7 +1455,7 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'] {
|
input[type="text"] {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1732,7 +1752,7 @@
|
|||||||
border-width: 0 0 1px;
|
border-width: 0 0 1px;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -5px;
|
bottom: -5px;
|
||||||
right: 0;
|
right: 0;
|
||||||
@ -1788,7 +1808,7 @@
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
font-family: 'Roboto Mono', monospace;
|
font-family: "Roboto Mono", monospace;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.714285714;
|
line-height: 1.714285714;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
@ -1822,7 +1842,7 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
&[type='checkbox'] {
|
&[type="checkbox"] {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1834,7 +1854,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
&[type='checkbox'] {
|
&[type="checkbox"] {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1912,16 +1932,11 @@
|
|||||||
|
|
||||||
.media-embed-wrap {
|
.media-embed-wrap {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #000;
|
|
||||||
|
|
||||||
.player-container,
|
.player-container,
|
||||||
.player-container-inner {
|
.player-container-inner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-top: 0;
|
|
||||||
background: #000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@ -1932,7 +1947,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.video-js.vjs-mediacms {
|
.video-js.vjs-mediacms {
|
||||||
padding-top: 0;
|
padding-top: math.div(9, 16) * 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1984,8 +1999,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.item-date:before {
|
.item-date:before {
|
||||||
content: '•';
|
content: "•";
|
||||||
content: '\2022';
|
content: "\2022";
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2022,14 +2037,14 @@
|
|||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: ',';
|
content: ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { SiteContext } from '../../utils/contexts/';
|
|||||||
import { useUser, usePopup } from '../../utils/hooks/';
|
import { useUser, usePopup } from '../../utils/hooks/';
|
||||||
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
||||||
import { PageActions, MediaPageActions } from '../../utils/actions/';
|
import { PageActions, MediaPageActions } from '../../utils/actions/';
|
||||||
import { formatInnerLink, publishedOnDate } from '../../utils/helpers/';
|
import { formatInnerLink, inEmbeddedApp, publishedOnDate } from '../../utils/helpers/';
|
||||||
import { PopupMain } from '../_shared/';
|
import { PopupMain } from '../_shared/';
|
||||||
import CommentsList from '../comments/Comments';
|
import CommentsList from '../comments/Comments';
|
||||||
import { replaceString } from '../../utils/helpers/';
|
import { replaceString } from '../../utils/helpers/';
|
||||||
@ -125,7 +125,9 @@ export default function ViewerInfoContent(props) {
|
|||||||
PageActions.addNotification('Media removed. Redirecting...', 'mediaDelete');
|
PageActions.addNotification('Media removed. Redirecting...', 'mediaDelete');
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
window.location.href =
|
window.location.href =
|
||||||
SiteContext._currentValue.url + '/' + MediaPageStore.get('media-data').author_profile.replace(/^\//g, '');
|
SiteContext._currentValue.url +
|
||||||
|
'/' +
|
||||||
|
MediaPageStore.get('media-data').author_profile.replace(/^\//g, '');
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
@ -185,7 +187,12 @@ export default function ViewerInfoContent(props) {
|
|||||||
{void 0 === PageStore.get('config-media-item').displayAuthor ||
|
{void 0 === PageStore.get('config-media-item').displayAuthor ||
|
||||||
null === PageStore.get('config-media-item').displayAuthor ||
|
null === PageStore.get('config-media-item').displayAuthor ||
|
||||||
!!PageStore.get('config-media-item').displayAuthor ? (
|
!!PageStore.get('config-media-item').displayAuthor ? (
|
||||||
<MediaAuthorBanner link={authorLink} thumb={authorThumb} name={props.author.name} published={props.published} />
|
<MediaAuthorBanner
|
||||||
|
link={authorLink}
|
||||||
|
thumb={authorThumb}
|
||||||
|
name={props.author.name}
|
||||||
|
published={props.published}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="media-content-banner">
|
<div className="media-content-banner">
|
||||||
@ -212,14 +219,20 @@ export default function ViewerInfoContent(props) {
|
|||||||
{categoriesContent.length ? (
|
{categoriesContent.length ? (
|
||||||
<MediaMetaField
|
<MediaMetaField
|
||||||
value={categoriesContent}
|
value={categoriesContent}
|
||||||
title={1 < categoriesContent.length ? translateString('Categories') : translateString('Category')}
|
title={
|
||||||
|
1 < categoriesContent.length
|
||||||
|
? translateString('Categories')
|
||||||
|
: translateString('Category')
|
||||||
|
}
|
||||||
id="categories"
|
id="categories"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{userCan.editMedia ? (
|
{userCan.editMedia ? (
|
||||||
<div className="media-author-actions">
|
<div className="media-author-actions">
|
||||||
{userCan.editMedia ? <EditMediaButton link={MediaPageStore.get('media-data').edit_url} /> : null}
|
{userCan.editMedia ? (
|
||||||
|
<EditMediaButton link={MediaPageStore.get('media-data').edit_url} />
|
||||||
|
) : null}
|
||||||
|
|
||||||
{userCan.deleteMedia ? (
|
{userCan.deleteMedia ? (
|
||||||
<PopupTrigger contentRef={popupContentRef}>
|
<PopupTrigger contentRef={popupContentRef}>
|
||||||
@ -234,14 +247,22 @@ export default function ViewerInfoContent(props) {
|
|||||||
<PopupMain>
|
<PopupMain>
|
||||||
<div className="popup-message">
|
<div className="popup-message">
|
||||||
<span className="popup-message-title">Media removal</span>
|
<span className="popup-message-title">Media removal</span>
|
||||||
<span className="popup-message-main">You're willing to remove media permanently?</span>
|
<span className="popup-message-main">
|
||||||
|
You're willing to remove media permanently?
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<span className="popup-message-bottom">
|
<span className="popup-message-bottom">
|
||||||
<button className="button-link cancel-comment-removal" onClick={cancelMediaRemoval}>
|
<button
|
||||||
|
className="button-link cancel-comment-removal"
|
||||||
|
onClick={cancelMediaRemoval}
|
||||||
|
>
|
||||||
CANCEL
|
CANCEL
|
||||||
</button>
|
</button>
|
||||||
<button className="button-link proceed-comment-removal" onClick={proceedMediaRemoval}>
|
<button
|
||||||
|
className="button-link proceed-comment-removal"
|
||||||
|
onClick={proceedMediaRemoval}
|
||||||
|
>
|
||||||
PROCEED
|
PROCEED
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
@ -253,7 +274,7 @@ export default function ViewerInfoContent(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CommentsList />
|
{!inEmbeddedApp() && <CommentsList />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { formatViewsNumber } from '../../utils/helpers/';
|
import { formatViewsNumber, inEmbeddedApp } from '../../utils/helpers/';
|
||||||
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
||||||
import { MemberContext, PlaylistsContext } from '../../utils/contexts/';
|
import { MemberContext, PlaylistsContext } from '../../utils/contexts/';
|
||||||
import { MediaLikeIcon, MediaDislikeIcon, OtherMediaDownloadLink, VideoMediaDownloadLink, MediaSaveButton, MediaShareButton, MediaMoreOptionsIcon } from '../media-actions/';
|
import {
|
||||||
|
MediaLikeIcon,
|
||||||
|
MediaDislikeIcon,
|
||||||
|
OtherMediaDownloadLink,
|
||||||
|
VideoMediaDownloadLink,
|
||||||
|
MediaSaveButton,
|
||||||
|
MediaShareButton,
|
||||||
|
MediaMoreOptionsIcon,
|
||||||
|
} from '../media-actions/';
|
||||||
import ViewerInfoTitleBanner from './ViewerInfoTitleBanner';
|
import ViewerInfoTitleBanner from './ViewerInfoTitleBanner';
|
||||||
import { translateString } from '../../utils/helpers/';
|
import { translateString } from '../../utils/helpers/';
|
||||||
|
|
||||||
@ -74,7 +82,8 @@ export default class ViewerInfoVideoTitleBanner extends ViewerInfoTitleBanner {
|
|||||||
|
|
||||||
{displayViews ? (
|
{displayViews ? (
|
||||||
<div className="media-views">
|
<div className="media-views">
|
||||||
{formatViewsNumber(this.props.views, true)} {1 >= this.props.views ? translateString('view') : translateString('views')}
|
{formatViewsNumber(this.props.views, true)}{' '}
|
||||||
|
{1 >= this.props.views ? translateString('view') : translateString('views')}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
@ -82,9 +91,12 @@ export default class ViewerInfoVideoTitleBanner extends ViewerInfoTitleBanner {
|
|||||||
<div>
|
<div>
|
||||||
{MemberContext._currentValue.can.likeMedia ? <MediaLikeIcon /> : null}
|
{MemberContext._currentValue.can.likeMedia ? <MediaLikeIcon /> : null}
|
||||||
{MemberContext._currentValue.can.dislikeMedia ? <MediaDislikeIcon /> : null}
|
{MemberContext._currentValue.can.dislikeMedia ? <MediaDislikeIcon /> : null}
|
||||||
{MemberContext._currentValue.can.shareMedia ? <MediaShareButton isVideo={true} /> : null}
|
{!inEmbeddedApp() && MemberContext._currentValue.can.shareMedia ? (
|
||||||
|
<MediaShareButton isVideo={true} />
|
||||||
|
) : null}
|
||||||
|
|
||||||
{!MemberContext._currentValue.is.anonymous &&
|
{!inEmbeddedApp() &&
|
||||||
|
!MemberContext._currentValue.is.anonymous &&
|
||||||
MemberContext._currentValue.can.saveMedia &&
|
MemberContext._currentValue.can.saveMedia &&
|
||||||
-1 < PlaylistsContext._currentValue.mediaTypes.indexOf(MediaPageStore.get('media-type')) ? (
|
-1 < PlaylistsContext._currentValue.mediaTypes.indexOf(MediaPageStore.get('media-type')) ? (
|
||||||
<MediaSaveButton />
|
<MediaSaveButton />
|
||||||
|
|||||||
@ -410,12 +410,8 @@ export default class VideoViewer extends React.PureComponent {
|
|||||||
poster: this.videoPoster,
|
poster: this.videoPoster,
|
||||||
previewSprite: previewSprite,
|
previewSprite: previewSprite,
|
||||||
subtitlesInfo: this.props.data.subtitles_info,
|
subtitlesInfo: this.props.data.subtitles_info,
|
||||||
|
enableAutoplay: !this.props.inEmbed,
|
||||||
inEmbed: this.props.inEmbed,
|
inEmbed: this.props.inEmbed,
|
||||||
showTitle: this.props.showTitle,
|
|
||||||
showRelated: this.props.showRelated,
|
|
||||||
showUserAvatar: this.props.showUserAvatar,
|
|
||||||
linkTitle: this.props.linkTitle,
|
|
||||||
urlTimestamp: this.props.timestamp,
|
|
||||||
hasTheaterMode: !this.props.inEmbed,
|
hasTheaterMode: !this.props.inEmbed,
|
||||||
hasNextLink: !!nextLink,
|
hasNextLink: !!nextLink,
|
||||||
nextLink: nextLink,
|
nextLink: nextLink,
|
||||||
@ -439,19 +435,9 @@ export default class VideoViewer extends React.PureComponent {
|
|||||||
|
|
||||||
VideoViewer.defaultProps = {
|
VideoViewer.defaultProps = {
|
||||||
inEmbed: !0,
|
inEmbed: !0,
|
||||||
showTitle: !0,
|
|
||||||
showRelated: !0,
|
|
||||||
showUserAvatar: !0,
|
|
||||||
linkTitle: !0,
|
|
||||||
timestamp: null,
|
|
||||||
siteUrl: PropTypes.string.isRequired,
|
siteUrl: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
VideoViewer.propTypes = {
|
VideoViewer.propTypes = {
|
||||||
inEmbed: PropTypes.bool,
|
inEmbed: PropTypes.bool,
|
||||||
showTitle: PropTypes.bool,
|
|
||||||
showRelated: PropTypes.bool,
|
|
||||||
showUserAvatar: PropTypes.bool,
|
|
||||||
linkTitle: PropTypes.bool,
|
|
||||||
timestamp: PropTypes.number,
|
|
||||||
};
|
};
|
||||||
@ -23,6 +23,11 @@
|
|||||||
transition-property: padding-left;
|
transition-property: padding-left;
|
||||||
transition-duration: 0.2s;
|
transition-duration: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.embedded-app & {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#page-profile-media,
|
#page-profile-media,
|
||||||
|
|||||||
@ -162,12 +162,16 @@ class ProfileSearchBar extends React.PureComponent {
|
|||||||
|
|
||||||
if (!this.state.visibleForm) {
|
if (!this.state.visibleForm) {
|
||||||
return (
|
return (
|
||||||
<span style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', position: 'relative' }} onClick={this.showForm}>
|
<span
|
||||||
|
style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', position: 'relative' }}
|
||||||
|
onClick={this.showForm}
|
||||||
|
>
|
||||||
<CircleIconButton buttonShadow={false}>
|
<CircleIconButton buttonShadow={false}>
|
||||||
<i className="material-icons">search</i>
|
<i className="material-icons">search</i>
|
||||||
</CircleIconButton>
|
</CircleIconButton>
|
||||||
{hasSearchText ? (
|
{hasSearchText ? (
|
||||||
<span style={{
|
<span
|
||||||
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '8px',
|
top: '8px',
|
||||||
right: '8px',
|
right: '8px',
|
||||||
@ -176,7 +180,8 @@ class ProfileSearchBar extends React.PureComponent {
|
|||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor: 'var(--default-theme-color)',
|
backgroundColor: 'var(--default-theme-color)',
|
||||||
border: '2px solid white',
|
border: '2px solid white',
|
||||||
}}></span>
|
}}
|
||||||
|
></span>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -189,7 +194,8 @@ class ProfileSearchBar extends React.PureComponent {
|
|||||||
<i className="material-icons">search</i>
|
<i className="material-icons">search</i>
|
||||||
</CircleIconButton>
|
</CircleIconButton>
|
||||||
{hasSearchText ? (
|
{hasSearchText ? (
|
||||||
<span style={{
|
<span
|
||||||
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '8px',
|
top: '8px',
|
||||||
right: '8px',
|
right: '8px',
|
||||||
@ -198,7 +204,8 @@ class ProfileSearchBar extends React.PureComponent {
|
|||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor: 'var(--default-theme-color)',
|
backgroundColor: 'var(--default-theme-color)',
|
||||||
border: '2px solid white',
|
border: '2px solid white',
|
||||||
}}></span>
|
}}
|
||||||
|
></span>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
@ -427,17 +434,32 @@ class NavMenuInlineTabs extends React.PureComponent {
|
|||||||
|
|
||||||
{!['about', 'playlists'].includes(this.props.type) ? (
|
{!['about', 'playlists'].includes(this.props.type) ? (
|
||||||
<li className="media-search">
|
<li className="media-search">
|
||||||
<ProfileSearchBar onQueryChange={this.props.onQueryChange} toggleSearchField={this.onToggleSearchField} type={this.props.type} />
|
<ProfileSearchBar
|
||||||
|
onQueryChange={this.props.onQueryChange}
|
||||||
|
toggleSearchField={this.onToggleSearchField}
|
||||||
|
type={this.props.type}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
) : null}
|
) : null}
|
||||||
{this.props.onToggleFiltersClick && ['media', 'shared_by_me', 'shared_with_me'].includes(this.props.type) ? (
|
{this.props.onToggleFiltersClick &&
|
||||||
|
['media', 'shared_by_me', 'shared_with_me'].includes(this.props.type) ? (
|
||||||
<li className="media-filters-toggle">
|
<li className="media-filters-toggle">
|
||||||
<span style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', position: 'relative' }} onClick={this.props.onToggleFiltersClick} title={translateString('Filters')}>
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
onClick={this.props.onToggleFiltersClick}
|
||||||
|
title={translateString('Filters')}
|
||||||
|
>
|
||||||
<CircleIconButton buttonShadow={false}>
|
<CircleIconButton buttonShadow={false}>
|
||||||
<i className="material-icons">filter_list</i>
|
<i className="material-icons">filter_list</i>
|
||||||
</CircleIconButton>
|
</CircleIconButton>
|
||||||
{this.props.hasActiveFilters ? (
|
{this.props.hasActiveFilters ? (
|
||||||
<span style={{
|
<span
|
||||||
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '8px',
|
top: '8px',
|
||||||
right: '8px',
|
right: '8px',
|
||||||
@ -446,19 +468,31 @@ class NavMenuInlineTabs extends React.PureComponent {
|
|||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor: 'var(--default-theme-color)',
|
backgroundColor: 'var(--default-theme-color)',
|
||||||
border: '2px solid white',
|
border: '2px solid white',
|
||||||
}}></span>
|
}}
|
||||||
|
></span>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
) : null}
|
) : null}
|
||||||
{this.props.onToggleTagsClick && ['media', 'shared_by_me', 'shared_with_me'].includes(this.props.type) ? (
|
{this.props.onToggleTagsClick &&
|
||||||
|
['media', 'shared_by_me', 'shared_with_me'].includes(this.props.type) ? (
|
||||||
<li className="media-tags-toggle">
|
<li className="media-tags-toggle">
|
||||||
<span style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', position: 'relative' }} onClick={this.props.onToggleTagsClick} title={translateString('Tags')}>
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
onClick={this.props.onToggleTagsClick}
|
||||||
|
title={translateString('Tags')}
|
||||||
|
>
|
||||||
<CircleIconButton buttonShadow={false}>
|
<CircleIconButton buttonShadow={false}>
|
||||||
<i className="material-icons">local_offer</i>
|
<i className="material-icons">local_offer</i>
|
||||||
</CircleIconButton>
|
</CircleIconButton>
|
||||||
{this.props.hasActiveTags ? (
|
{this.props.hasActiveTags ? (
|
||||||
<span style={{
|
<span
|
||||||
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '8px',
|
top: '8px',
|
||||||
right: '8px',
|
right: '8px',
|
||||||
@ -467,19 +501,31 @@ class NavMenuInlineTabs extends React.PureComponent {
|
|||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor: 'var(--default-theme-color)',
|
backgroundColor: 'var(--default-theme-color)',
|
||||||
border: '2px solid white',
|
border: '2px solid white',
|
||||||
}}></span>
|
}}
|
||||||
|
></span>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
) : null}
|
) : null}
|
||||||
{this.props.onToggleSortingClick && ['media', 'shared_by_me', 'shared_with_me'].includes(this.props.type) ? (
|
{this.props.onToggleSortingClick &&
|
||||||
|
['media', 'shared_by_me', 'shared_with_me'].includes(this.props.type) ? (
|
||||||
<li className="media-sorting-toggle">
|
<li className="media-sorting-toggle">
|
||||||
<span style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', position: 'relative' }} onClick={this.props.onToggleSortingClick} title={translateString('Sort By')}>
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
onClick={this.props.onToggleSortingClick}
|
||||||
|
title={translateString('Sort By')}
|
||||||
|
>
|
||||||
<CircleIconButton buttonShadow={false}>
|
<CircleIconButton buttonShadow={false}>
|
||||||
<i className="material-icons">swap_vert</i>
|
<i className="material-icons">swap_vert</i>
|
||||||
</CircleIconButton>
|
</CircleIconButton>
|
||||||
{this.props.hasActiveSort ? (
|
{this.props.hasActiveSort ? (
|
||||||
<span style={{
|
<span
|
||||||
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '8px',
|
top: '8px',
|
||||||
right: '8px',
|
right: '8px',
|
||||||
@ -488,7 +534,8 @@ class NavMenuInlineTabs extends React.PureComponent {
|
|||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor: 'var(--default-theme-color)',
|
backgroundColor: 'var(--default-theme-color)',
|
||||||
border: '2px solid white',
|
border: '2px solid white',
|
||||||
}}></span>
|
}}
|
||||||
|
></span>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
@ -658,6 +705,7 @@ export default function ProfilePagesHeader(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={profilePageHeaderRef} className={'profile-page-header' + (fixedNav ? ' fixed-nav' : '')}>
|
<div ref={profilePageHeaderRef} className={'profile-page-header' + (fixedNav ? ' fixed-nav' : '')}>
|
||||||
|
{!props.hideChannelBanner && (
|
||||||
<span className="profile-banner-wrap">
|
<span className="profile-banner-wrap">
|
||||||
{props.author.banner_thumbnail_url ? (
|
{props.author.banner_thumbnail_url ? (
|
||||||
<span
|
<span
|
||||||
@ -685,14 +733,22 @@ export default function ProfilePagesHeader(props) {
|
|||||||
<PopupMain>
|
<PopupMain>
|
||||||
<div className="popup-message">
|
<div className="popup-message">
|
||||||
<span className="popup-message-title">Profile removal</span>
|
<span className="popup-message-title">Profile removal</span>
|
||||||
<span className="popup-message-main">You're willing to remove profile permanently?</span>
|
<span className="popup-message-main">
|
||||||
|
You're willing to remove profile permanently?
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<span className="popup-message-bottom">
|
<span className="popup-message-bottom">
|
||||||
<button className="button-link cancel-profile-removal" onClick={cancelProfileRemoval}>
|
<button
|
||||||
|
className="button-link cancel-profile-removal"
|
||||||
|
onClick={cancelProfileRemoval}
|
||||||
|
>
|
||||||
CANCEL
|
CANCEL
|
||||||
</button>
|
</button>
|
||||||
<button className="button-link proceed-profile-removal" onClick={proceedMediaRemoval}>
|
<button
|
||||||
|
className="button-link proceed-profile-removal"
|
||||||
|
onClick={proceedMediaRemoval}
|
||||||
|
>
|
||||||
PROCEED
|
PROCEED
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
@ -709,17 +765,22 @@ export default function ProfilePagesHeader(props) {
|
|||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="profile-info-nav-wrap">
|
<div className="profile-info-nav-wrap">
|
||||||
{props.author.thumbnail_url || props.author.name ? (
|
{props.author.thumbnail_url || props.author.name ? (
|
||||||
<div className="profile-info">
|
<div className="profile-info">
|
||||||
<div className="profile-info-inner">
|
<div className="profile-info-inner">
|
||||||
<div>{props.author.thumbnail_url ? <img src={props.author.thumbnail_url} alt="" /> : null}</div>
|
<div>
|
||||||
|
{props.author.thumbnail_url ? <img src={props.author.thumbnail_url} alt="" /> : null}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{props.author.name ? (
|
{props.author.name ? (
|
||||||
<div className="profile-name-edit-wrapper">
|
<div className="profile-name-edit-wrapper">
|
||||||
<h1>{props.author.name}</h1>
|
<h1>{props.author.name}</h1>
|
||||||
{userCanEditProfile && !userIsAuthor ? <EditProfileButton link={ProfilePageStore.get('author-data').edit_url} /> : null}
|
{userCanEditProfile && !userIsAuthor ? (
|
||||||
|
<EditProfileButton link={ProfilePageStore.get('author-data').edit_url} />
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const EmbedPage: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="embed-wrap media-embed-wrap" style={wrapperStyles}>
|
<div className="embed-wrap" style={wrapperStyles}>
|
||||||
{failedMediaLoad && (
|
{failedMediaLoad && (
|
||||||
<div className="player-container player-container-error" style={containerStyles}>
|
<div className="player-container player-container-error" style={containerStyles}>
|
||||||
<div className="player-container-inner" style={containerStyles}>
|
<div className="player-container-inner" style={containerStyles}>
|
||||||
@ -59,32 +59,9 @@ export const EmbedPage: React.FC = () => {
|
|||||||
|
|
||||||
{loadedVideo && (
|
{loadedVideo && (
|
||||||
<SiteConsumer>
|
<SiteConsumer>
|
||||||
{(site) => {
|
{(site) => (
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
<VideoViewer data={MediaPageStore.get('media-data')} siteUrl={site.url} containerStyles={containerStyles} />
|
||||||
const urlShowTitle = urlParams.get('showTitle');
|
)}
|
||||||
const showTitle = urlShowTitle !== '0';
|
|
||||||
const urlShowRelated = urlParams.get('showRelated');
|
|
||||||
const showRelated = urlShowRelated !== '0';
|
|
||||||
const urlShowUserAvatar = urlParams.get('showUserAvatar');
|
|
||||||
const showUserAvatar = urlShowUserAvatar !== '0';
|
|
||||||
const urlLinkTitle = urlParams.get('linkTitle');
|
|
||||||
const linkTitle = urlLinkTitle !== '0';
|
|
||||||
const urlTimestamp = urlParams.get('t');
|
|
||||||
const timestamp = urlTimestamp ? parseInt(urlTimestamp, 10) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<VideoViewer
|
|
||||||
data={MediaPageStore.get('media-data')}
|
|
||||||
siteUrl={site.url}
|
|
||||||
containerStyles={containerStyles}
|
|
||||||
showTitle={showTitle}
|
|
||||||
showRelated={showRelated}
|
|
||||||
showUserAvatar={showUserAvatar}
|
|
||||||
linkTitle={linkTitle}
|
|
||||||
timestamp={timestamp}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</SiteConsumer>
|
</SiteConsumer>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import UrlParse from 'url-parse';
|
import UrlParse from 'url-parse';
|
||||||
import { ApiUrlContext, MemberContext, SiteContext } from '../utils/contexts/';
|
import { ApiUrlContext, MemberContext, SiteContext } from '../utils/contexts/';
|
||||||
import { formatInnerLink, csrfToken, postRequest } from '../utils/helpers/';
|
import { formatInnerLink, csrfToken, postRequest, inEmbeddedApp } from '../utils/helpers/';
|
||||||
import { PageActions } from '../utils/actions/';
|
import { PageActions } from '../utils/actions/';
|
||||||
import { PageStore, ProfilePageStore } from '../utils/stores/';
|
import { PageStore, ProfilePageStore } from '../utils/stores/';
|
||||||
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
||||||
@ -268,7 +268,7 @@ export class ProfileAboutPage extends ProfileMediaPage {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="about" />
|
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="about" hideChannelBanner={inEmbeddedApp()} />
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesContent key="ProfilePagesContent" enabledContactForm={this.enabledContactForm}>
|
<ProfilePagesContent key="ProfilePagesContent" enabledContactForm={this.enabledContactForm}>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ApiUrlConsumer } from '../utils/contexts/';
|
import { ApiUrlConsumer } from '../utils/contexts/';
|
||||||
import { PageStore } from '../utils/stores/';
|
import { PageStore } from '../utils/stores/';
|
||||||
|
import { inEmbeddedApp } from '../utils/helpers/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
||||||
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
||||||
@ -28,7 +29,7 @@ export class ProfileHistoryPage extends ProfileMediaPage {
|
|||||||
pageContent() {
|
pageContent() {
|
||||||
return [
|
return [
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="history" />
|
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="history" hideChannelBanner={inEmbeddedApp()} />
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesContent key="ProfilePagesContent">
|
<ProfilePagesContent key="ProfilePagesContent">
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ApiUrlConsumer } from '../utils/contexts/';
|
import { ApiUrlConsumer } from '../utils/contexts/';
|
||||||
import { PageStore } from '../utils/stores/';
|
import { PageStore } from '../utils/stores/';
|
||||||
|
import { inEmbeddedApp } from '../utils/helpers/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
||||||
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
||||||
@ -28,7 +29,7 @@ export class ProfileLikedPage extends ProfileMediaPage {
|
|||||||
pageContent() {
|
pageContent() {
|
||||||
return [
|
return [
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="liked" />
|
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="liked" hideChannelBanner={inEmbeddedApp()} />
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesContent key="ProfilePagesContent">
|
<ProfilePagesContent key="ProfilePagesContent">
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { ApiUrlContext, LinksConsumer, MemberContext } from '../utils/contexts';
|
import { ApiUrlContext, LinksConsumer, MemberContext } from '../utils/contexts';
|
||||||
import { PageStore, ProfilePageStore } from '../utils/stores';
|
import { PageStore, ProfilePageStore } from '../utils/stores';
|
||||||
import { ProfilePageActions, PageActions } from '../utils/actions';
|
import { ProfilePageActions, PageActions } from '../utils/actions';
|
||||||
import { translateString } from '../utils/helpers/';
|
import { inEmbeddedApp, translateString } from '../utils/helpers/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
||||||
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
||||||
@ -120,7 +120,13 @@ export class ProfileMediaPage extends Page {
|
|||||||
|
|
||||||
if (author) {
|
if (author) {
|
||||||
if (this.state.query) {
|
if (this.state.query) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
author.id +
|
||||||
|
'&q=' +
|
||||||
|
encodeURIComponent(this.state.query) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} else {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + this.state.filterArgs;
|
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + this.state.filterArgs;
|
||||||
}
|
}
|
||||||
@ -171,7 +177,13 @@ export class ProfileMediaPage extends Page {
|
|||||||
let requestUrl;
|
let requestUrl;
|
||||||
|
|
||||||
if (newQuery) {
|
if (newQuery) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&q=' + encodeURIComponent(newQuery) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
this.state.author.id +
|
||||||
|
'&q=' +
|
||||||
|
encodeURIComponent(newQuery) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} 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;
|
||||||
}
|
}
|
||||||
@ -212,37 +224,55 @@ export class ProfileMediaPage extends Page {
|
|||||||
this.setState({
|
this.setState({
|
||||||
showConfirmModal: true,
|
showConfirmModal: true,
|
||||||
pendingAction: action,
|
pendingAction: action,
|
||||||
confirmMessage: translateString('You are going to delete') + ` ${selectedCount} ` + translateString('media, are you sure?'),
|
confirmMessage:
|
||||||
|
translateString('You are going to delete') +
|
||||||
|
` ${selectedCount} ` +
|
||||||
|
translateString('media, are you sure?'),
|
||||||
});
|
});
|
||||||
} else if (action === 'enable-comments') {
|
} else if (action === 'enable-comments') {
|
||||||
this.setState({
|
this.setState({
|
||||||
showConfirmModal: true,
|
showConfirmModal: true,
|
||||||
pendingAction: action,
|
pendingAction: action,
|
||||||
confirmMessage: translateString('You are going to enable comments to') + ` ${selectedCount} ` + translateString('media, are you sure?'),
|
confirmMessage:
|
||||||
|
translateString('You are going to enable comments to') +
|
||||||
|
` ${selectedCount} ` +
|
||||||
|
translateString('media, are you sure?'),
|
||||||
});
|
});
|
||||||
} else if (action === 'disable-comments') {
|
} else if (action === 'disable-comments') {
|
||||||
this.setState({
|
this.setState({
|
||||||
showConfirmModal: true,
|
showConfirmModal: true,
|
||||||
pendingAction: action,
|
pendingAction: action,
|
||||||
confirmMessage: translateString('You are going to disable comments to') + ` ${selectedCount} ` + translateString('media, are you sure?'),
|
confirmMessage:
|
||||||
|
translateString('You are going to disable comments to') +
|
||||||
|
` ${selectedCount} ` +
|
||||||
|
translateString('media, are you sure?'),
|
||||||
});
|
});
|
||||||
} else if (action === 'enable-download') {
|
} else if (action === 'enable-download') {
|
||||||
this.setState({
|
this.setState({
|
||||||
showConfirmModal: true,
|
showConfirmModal: true,
|
||||||
pendingAction: action,
|
pendingAction: action,
|
||||||
confirmMessage: translateString('You are going to enable download for') + ` ${selectedCount} ` + translateString('media, are you sure?'),
|
confirmMessage:
|
||||||
|
translateString('You are going to enable download for') +
|
||||||
|
` ${selectedCount} ` +
|
||||||
|
translateString('media, are you sure?'),
|
||||||
});
|
});
|
||||||
} else if (action === 'disable-download') {
|
} else if (action === 'disable-download') {
|
||||||
this.setState({
|
this.setState({
|
||||||
showConfirmModal: true,
|
showConfirmModal: true,
|
||||||
pendingAction: action,
|
pendingAction: action,
|
||||||
confirmMessage: translateString('You are going to disable download for') + ` ${selectedCount} ` + translateString('media, are you sure?'),
|
confirmMessage:
|
||||||
|
translateString('You are going to disable download for') +
|
||||||
|
` ${selectedCount} ` +
|
||||||
|
translateString('media, are you sure?'),
|
||||||
});
|
});
|
||||||
} else if (action === 'copy-media') {
|
} else if (action === 'copy-media') {
|
||||||
this.setState({
|
this.setState({
|
||||||
showConfirmModal: true,
|
showConfirmModal: true,
|
||||||
pendingAction: action,
|
pendingAction: action,
|
||||||
confirmMessage: translateString('You are going to copy') + ` ${selectedCount} ` + translateString('media, are you sure?'),
|
confirmMessage:
|
||||||
|
translateString('You are going to copy') +
|
||||||
|
` ${selectedCount} ` +
|
||||||
|
translateString('media, are you sure?'),
|
||||||
});
|
});
|
||||||
} else if (action === 'add-remove-coviewers') {
|
} else if (action === 'add-remove-coviewers') {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -337,7 +367,8 @@ export class ProfileMediaPage extends Page {
|
|||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const message = selectedCount === 1
|
const message =
|
||||||
|
selectedCount === 1
|
||||||
? translateString('The media was deleted successfully.')
|
? translateString('The media was deleted successfully.')
|
||||||
: translateString('Successfully deleted') + ` ${selectedCount} ` + translateString('media.');
|
: translateString('Successfully deleted') + ` ${selectedCount} ` + translateString('media.');
|
||||||
this.showNotification(message);
|
this.showNotification(message);
|
||||||
@ -590,10 +621,18 @@ export class ProfileMediaPage extends Page {
|
|||||||
this.setState({ selectedTag: tag }, () => {
|
this.setState({ selectedTag: tag }, () => {
|
||||||
// Apply tag filter
|
// Apply tag filter
|
||||||
this.onFiltersUpdate({
|
this.onFiltersUpdate({
|
||||||
media_type: this.state.filterArgs.includes('media_type') ? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1] : null,
|
media_type: this.state.filterArgs.includes('media_type')
|
||||||
upload_date: this.state.filterArgs.includes('upload_date') ? this.state.filterArgs.match(/upload_date=([^&]*)/)?.[1] : null,
|
? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1]
|
||||||
duration: this.state.filterArgs.includes('duration') ? this.state.filterArgs.match(/duration=([^&]*)/)?.[1] : null,
|
: null,
|
||||||
publish_state: this.state.filterArgs.includes('publish_state') ? this.state.filterArgs.match(/publish_state=([^&]*)/)?.[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,
|
sort_by: this.state.selectedSort,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
});
|
});
|
||||||
@ -604,10 +643,18 @@ export class ProfileMediaPage extends Page {
|
|||||||
this.setState({ selectedSort: sortOption }, () => {
|
this.setState({ selectedSort: sortOption }, () => {
|
||||||
// Apply sort filter
|
// Apply sort filter
|
||||||
this.onFiltersUpdate({
|
this.onFiltersUpdate({
|
||||||
media_type: this.state.filterArgs.includes('media_type') ? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1] : null,
|
media_type: this.state.filterArgs.includes('media_type')
|
||||||
upload_date: this.state.filterArgs.includes('upload_date') ? this.state.filterArgs.match(/upload_date=([^&]*)/)?.[1] : null,
|
? this.state.filterArgs.match(/media_type=([^&]*)/)?.[1]
|
||||||
duration: this.state.filterArgs.includes('duration') ? this.state.filterArgs.match(/duration=([^&]*)/)?.[1] : null,
|
: null,
|
||||||
publish_state: this.state.filterArgs.includes('publish_state') ? this.state.filterArgs.match(/publish_state=([^&]*)/)?.[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,
|
sort_by: sortOption,
|
||||||
tag: this.state.selectedTag,
|
tag: this.state.selectedTag,
|
||||||
});
|
});
|
||||||
@ -707,9 +754,16 @@ export class ProfileMediaPage extends Page {
|
|||||||
let requestUrl;
|
let requestUrl;
|
||||||
|
|
||||||
if (this.state.query) {
|
if (this.state.query) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
this.state.author.id +
|
||||||
|
'&q=' +
|
||||||
|
encodeURIComponent(this.state.query) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -851,7 +905,10 @@ export class ProfileMediaPage extends Page {
|
|||||||
onResponseDataLoaded(responseData) {
|
onResponseDataLoaded(responseData) {
|
||||||
// Extract tags from response
|
// Extract tags from response
|
||||||
if (responseData && responseData.tags) {
|
if (responseData && responseData.tags) {
|
||||||
const tags = responseData.tags.split(',').map((tag) => tag.trim()).filter((tag) => tag);
|
const tags = responseData.tags
|
||||||
|
.split(',')
|
||||||
|
.map((tag) => tag.trim())
|
||||||
|
.filter((tag) => tag);
|
||||||
this.setState({ availableTags: tags });
|
this.setState({ availableTags: tags });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -862,12 +919,12 @@ export class ProfileMediaPage extends Page {
|
|||||||
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
||||||
|
|
||||||
// Check if any filters are active (excluding default sort and tags)
|
// Check if any filters are active (excluding default sort and tags)
|
||||||
const hasActiveFilters = this.state.filterArgs && (
|
const hasActiveFilters =
|
||||||
this.state.filterArgs.includes('media_type=') ||
|
this.state.filterArgs &&
|
||||||
|
(this.state.filterArgs.includes('media_type=') ||
|
||||||
this.state.filterArgs.includes('upload_date=') ||
|
this.state.filterArgs.includes('upload_date=') ||
|
||||||
this.state.filterArgs.includes('duration=') ||
|
this.state.filterArgs.includes('duration=') ||
|
||||||
this.state.filterArgs.includes('publish_state=')
|
this.state.filterArgs.includes('publish_state='));
|
||||||
);
|
|
||||||
|
|
||||||
const hasActiveTags = this.state.selectedTag && this.state.selectedTag !== 'all';
|
const hasActiveTags = this.state.selectedTag && this.state.selectedTag !== 'all';
|
||||||
const hasActiveSort = this.state.selectedSort && this.state.selectedSort !== 'date_added_desc';
|
const hasActiveSort = this.state.selectedSort && this.state.selectedSort !== 'date_added_desc';
|
||||||
@ -885,6 +942,7 @@ export class ProfileMediaPage extends Page {
|
|||||||
hasActiveFilters={hasActiveFilters}
|
hasActiveFilters={hasActiveFilters}
|
||||||
hasActiveTags={hasActiveTags}
|
hasActiveTags={hasActiveTags}
|
||||||
hasActiveSort={hasActiveSort}
|
hasActiveSort={hasActiveSort}
|
||||||
|
hideChannelBanner={inEmbeddedApp()}
|
||||||
/>
|
/>
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
@ -900,8 +958,18 @@ export class ProfileMediaPage extends Page {
|
|||||||
onDeselectAll={this.handleDeselectAll}
|
onDeselectAll={this.handleDeselectAll}
|
||||||
showAddMediaButton={isMediaAuthor}
|
showAddMediaButton={isMediaAuthor}
|
||||||
>
|
>
|
||||||
<ProfileMediaFilters hidden={this.state.hiddenFilters} tags={this.state.availableTags} onFiltersUpdate={this.onFiltersUpdate} selectedTag={this.state.selectedTag} selectedSort={this.state.selectedSort} />
|
<ProfileMediaFilters
|
||||||
<ProfileMediaTags hidden={this.state.hiddenTags} tags={this.state.availableTags} onTagSelect={this.onTagSelect} />
|
hidden={this.state.hiddenFilters}
|
||||||
|
tags={this.state.availableTags}
|
||||||
|
onFiltersUpdate={this.onFiltersUpdate}
|
||||||
|
selectedTag={this.state.selectedTag}
|
||||||
|
selectedSort={this.state.selectedSort}
|
||||||
|
/>
|
||||||
|
<ProfileMediaTags
|
||||||
|
hidden={this.state.hiddenTags}
|
||||||
|
tags={this.state.availableTags}
|
||||||
|
onTagSelect={this.onTagSelect}
|
||||||
|
/>
|
||||||
<ProfileMediaSorting hidden={this.state.hiddenSorting} onSortSelect={this.onSortSelect} />
|
<ProfileMediaSorting hidden={this.state.hiddenSorting} onSortSelect={this.onSortSelect} />
|
||||||
<LazyLoadItemListAsync
|
<LazyLoadItemListAsync
|
||||||
key={`${this.state.requestUrl}-${this.state.listKey}`}
|
key={`${this.state.requestUrl}-${this.state.listKey}`}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ApiUrlConsumer } from '../utils/contexts/';
|
import { ApiUrlConsumer } from '../utils/contexts/';
|
||||||
import { PageStore } from '../utils/stores/';
|
import { PageStore } from '../utils/stores/';
|
||||||
|
import { inEmbeddedApp } from '../utils/helpers/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
||||||
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
||||||
@ -30,7 +31,7 @@ export class ProfilePlaylistsPage extends ProfileMediaPage {
|
|||||||
pageContent() {
|
pageContent() {
|
||||||
return [
|
return [
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="playlists" />
|
<ProfilePagesHeader key="ProfilePagesHeader" author={this.state.author} type="playlists" hideChannelBanner={inEmbeddedApp()} />
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesContent key="ProfilePagesContent">
|
<ProfilePagesContent key="ProfilePagesContent">
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { ProfileMediaFilters } from '../components/search-filters/ProfileMediaFi
|
|||||||
import { ProfileMediaTags } from '../components/search-filters/ProfileMediaTags';
|
import { ProfileMediaTags } from '../components/search-filters/ProfileMediaTags';
|
||||||
import { ProfileMediaSorting } from '../components/search-filters/ProfileMediaSorting';
|
import { ProfileMediaSorting } from '../components/search-filters/ProfileMediaSorting';
|
||||||
import { BulkActionsModals } from '../components/BulkActionsModals';
|
import { BulkActionsModals } from '../components/BulkActionsModals';
|
||||||
import { translateString } from '../utils/helpers';
|
import { inEmbeddedApp, translateString } from '../utils/helpers';
|
||||||
import { withBulkActions } from '../utils/hoc/withBulkActions';
|
import { withBulkActions } from '../utils/hoc/withBulkActions';
|
||||||
|
|
||||||
import { Page } from './_Page';
|
import { Page } from './_Page';
|
||||||
@ -24,9 +24,7 @@ function EmptySharedByMe(props) {
|
|||||||
{(links) => (
|
{(links) => (
|
||||||
<div className="empty-media empty-channel-media">
|
<div className="empty-media empty-channel-media">
|
||||||
<div className="welcome-title">No shared media</div>
|
<div className="welcome-title">No shared media</div>
|
||||||
<div className="start-uploading">
|
<div className="start-uploading">Media that you have shared with others will show up here.</div>
|
||||||
Media that you have shared with others will show up here.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</LinksConsumer>
|
</LinksConsumer>
|
||||||
@ -81,9 +79,20 @@ class ProfileSharedByMePage extends Page {
|
|||||||
|
|
||||||
if (author) {
|
if (author) {
|
||||||
if (this.state.query) {
|
if (this.state.query) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_by_me&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
author.id +
|
||||||
|
'&show=shared_by_me&q=' +
|
||||||
|
encodeURIComponent(this.state.query) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} else {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_by_me' + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
author.id +
|
||||||
|
'&show=shared_by_me' +
|
||||||
|
this.state.filterArgs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +141,20 @@ class ProfileSharedByMePage extends Page {
|
|||||||
let requestUrl;
|
let requestUrl;
|
||||||
|
|
||||||
if (newQuery) {
|
if (newQuery) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_by_me&q=' + encodeURIComponent(newQuery) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
this.state.author.id +
|
||||||
|
'&show=shared_by_me&q=' +
|
||||||
|
encodeURIComponent(newQuery) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} 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;
|
let title = this.state.title;
|
||||||
@ -290,9 +310,20 @@ class ProfileSharedByMePage extends Page {
|
|||||||
let requestUrl;
|
let requestUrl;
|
||||||
|
|
||||||
if (this.state.query) {
|
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;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
this.state.author.id +
|
||||||
|
'&show=shared_by_me&q=' +
|
||||||
|
encodeURIComponent(this.state.query) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -304,7 +335,10 @@ class ProfileSharedByMePage extends Page {
|
|||||||
|
|
||||||
onResponseDataLoaded(responseData) {
|
onResponseDataLoaded(responseData) {
|
||||||
if (responseData && responseData.tags) {
|
if (responseData && responseData.tags) {
|
||||||
const tags = responseData.tags.split(',').map((tag) => tag.trim()).filter((tag) => tag);
|
const tags = responseData.tags
|
||||||
|
.split(',')
|
||||||
|
.map((tag) => tag.trim())
|
||||||
|
.filter((tag) => tag);
|
||||||
this.setState({ availableTags: tags });
|
this.setState({ availableTags: tags });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,12 +349,12 @@ class ProfileSharedByMePage extends Page {
|
|||||||
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
||||||
|
|
||||||
// Check if any filters are active
|
// Check if any filters are active
|
||||||
const hasActiveFilters = this.state.filterArgs && (
|
const hasActiveFilters =
|
||||||
this.state.filterArgs.includes('media_type=') ||
|
this.state.filterArgs &&
|
||||||
|
(this.state.filterArgs.includes('media_type=') ||
|
||||||
this.state.filterArgs.includes('upload_date=') ||
|
this.state.filterArgs.includes('upload_date=') ||
|
||||||
this.state.filterArgs.includes('duration=') ||
|
this.state.filterArgs.includes('duration=') ||
|
||||||
this.state.filterArgs.includes('publish_state=')
|
this.state.filterArgs.includes('publish_state='));
|
||||||
);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
@ -335,6 +369,7 @@ class ProfileSharedByMePage extends Page {
|
|||||||
hasActiveFilters={hasActiveFilters}
|
hasActiveFilters={hasActiveFilters}
|
||||||
hasActiveTags={this.state.selectedTag !== 'all'}
|
hasActiveTags={this.state.selectedTag !== 'all'}
|
||||||
hasActiveSort={this.state.selectedSort !== 'date_added_desc'}
|
hasActiveSort={this.state.selectedSort !== 'date_added_desc'}
|
||||||
|
hideChannelBanner={inEmbeddedApp()}
|
||||||
/>
|
/>
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
@ -349,8 +384,16 @@ class ProfileSharedByMePage extends Page {
|
|||||||
onSelectAll={this.props.bulkActions.handleSelectAll}
|
onSelectAll={this.props.bulkActions.handleSelectAll}
|
||||||
onDeselectAll={this.props.bulkActions.handleDeselectAll}
|
onDeselectAll={this.props.bulkActions.handleDeselectAll}
|
||||||
>
|
>
|
||||||
<ProfileMediaFilters hidden={this.state.hiddenFilters} tags={this.state.availableTags} onFiltersUpdate={this.onFiltersUpdate} />
|
<ProfileMediaFilters
|
||||||
<ProfileMediaTags hidden={this.state.hiddenTags} tags={this.state.availableTags} onTagSelect={this.onTagSelect} />
|
hidden={this.state.hiddenFilters}
|
||||||
|
tags={this.state.availableTags}
|
||||||
|
onFiltersUpdate={this.onFiltersUpdate}
|
||||||
|
/>
|
||||||
|
<ProfileMediaTags
|
||||||
|
hidden={this.state.hiddenTags}
|
||||||
|
tags={this.state.availableTags}
|
||||||
|
onTagSelect={this.onTagSelect}
|
||||||
|
/>
|
||||||
<ProfileMediaSorting hidden={this.state.hiddenSorting} onSortSelect={this.onSortSelect} />
|
<ProfileMediaSorting hidden={this.state.hiddenSorting} onSortSelect={this.onSortSelect} />
|
||||||
<LazyLoadItemListAsync
|
<LazyLoadItemListAsync
|
||||||
key={`${this.state.requestUrl}-${this.props.bulkActions.listKey}`}
|
key={`${this.state.requestUrl}-${this.props.bulkActions.listKey}`}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListA
|
|||||||
import { ProfileMediaFilters } from '../components/search-filters/ProfileMediaFilters';
|
import { ProfileMediaFilters } from '../components/search-filters/ProfileMediaFilters';
|
||||||
import { ProfileMediaTags } from '../components/search-filters/ProfileMediaTags';
|
import { ProfileMediaTags } from '../components/search-filters/ProfileMediaTags';
|
||||||
import { ProfileMediaSorting } from '../components/search-filters/ProfileMediaSorting';
|
import { ProfileMediaSorting } from '../components/search-filters/ProfileMediaSorting';
|
||||||
import { translateString } from '../utils/helpers';
|
import { inEmbeddedApp, translateString } from '../utils/helpers';
|
||||||
|
|
||||||
import { Page } from './_Page';
|
import { Page } from './_Page';
|
||||||
|
|
||||||
@ -22,9 +22,7 @@ function EmptySharedWithMe(props) {
|
|||||||
{(links) => (
|
{(links) => (
|
||||||
<div className="empty-media empty-channel-media">
|
<div className="empty-media empty-channel-media">
|
||||||
<div className="welcome-title">No shared media</div>
|
<div className="welcome-title">No shared media</div>
|
||||||
<div className="start-uploading">
|
<div className="start-uploading">Media that others have shared with you will show up here.</div>
|
||||||
Media that others have shared with you will show up here.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</LinksConsumer>
|
</LinksConsumer>
|
||||||
@ -79,9 +77,20 @@ export class ProfileSharedWithMePage extends Page {
|
|||||||
|
|
||||||
if (author) {
|
if (author) {
|
||||||
if (this.state.query) {
|
if (this.state.query) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_with_me&q=' + encodeURIComponent(this.state.query) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
author.id +
|
||||||
|
'&show=shared_with_me&q=' +
|
||||||
|
encodeURIComponent(this.state.query) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} else {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + author.id + '&show=shared_with_me' + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
author.id +
|
||||||
|
'&show=shared_with_me' +
|
||||||
|
this.state.filterArgs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,9 +139,20 @@ export class ProfileSharedWithMePage extends Page {
|
|||||||
let requestUrl;
|
let requestUrl;
|
||||||
|
|
||||||
if (newQuery) {
|
if (newQuery) {
|
||||||
requestUrl = ApiUrlContext._currentValue.media + '?author=' + this.state.author.id + '&show=shared_with_me&q=' + encodeURIComponent(newQuery) + this.state.filterArgs;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
this.state.author.id +
|
||||||
|
'&show=shared_with_me&q=' +
|
||||||
|
encodeURIComponent(newQuery) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} 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;
|
let title = this.state.title;
|
||||||
@ -288,9 +308,20 @@ export class ProfileSharedWithMePage extends Page {
|
|||||||
let requestUrl;
|
let requestUrl;
|
||||||
|
|
||||||
if (this.state.query) {
|
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;
|
requestUrl =
|
||||||
|
ApiUrlContext._currentValue.media +
|
||||||
|
'?author=' +
|
||||||
|
this.state.author.id +
|
||||||
|
'&show=shared_with_me&q=' +
|
||||||
|
encodeURIComponent(this.state.query) +
|
||||||
|
this.state.filterArgs;
|
||||||
} else {
|
} 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -302,7 +333,10 @@ export class ProfileSharedWithMePage extends Page {
|
|||||||
|
|
||||||
onResponseDataLoaded(responseData) {
|
onResponseDataLoaded(responseData) {
|
||||||
if (responseData && responseData.tags) {
|
if (responseData && responseData.tags) {
|
||||||
const tags = responseData.tags.split(',').map((tag) => tag.trim()).filter((tag) => tag);
|
const tags = responseData.tags
|
||||||
|
.split(',')
|
||||||
|
.map((tag) => tag.trim())
|
||||||
|
.filter((tag) => tag);
|
||||||
this.setState({ availableTags: tags });
|
this.setState({ availableTags: tags });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,12 +347,12 @@ export class ProfileSharedWithMePage extends Page {
|
|||||||
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
||||||
|
|
||||||
// Check if any filters are active
|
// Check if any filters are active
|
||||||
const hasActiveFilters = this.state.filterArgs && (
|
const hasActiveFilters =
|
||||||
this.state.filterArgs.includes('media_type=') ||
|
this.state.filterArgs &&
|
||||||
|
(this.state.filterArgs.includes('media_type=') ||
|
||||||
this.state.filterArgs.includes('upload_date=') ||
|
this.state.filterArgs.includes('upload_date=') ||
|
||||||
this.state.filterArgs.includes('duration=') ||
|
this.state.filterArgs.includes('duration=') ||
|
||||||
this.state.filterArgs.includes('publish_state=')
|
this.state.filterArgs.includes('publish_state='));
|
||||||
);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
@ -333,16 +367,22 @@ export class ProfileSharedWithMePage extends Page {
|
|||||||
hasActiveFilters={hasActiveFilters}
|
hasActiveFilters={hasActiveFilters}
|
||||||
hasActiveTags={this.state.selectedTag !== 'all'}
|
hasActiveTags={this.state.selectedTag !== 'all'}
|
||||||
hasActiveSort={this.state.selectedSort !== 'date_added_desc'}
|
hasActiveSort={this.state.selectedSort !== 'date_added_desc'}
|
||||||
|
hideChannelBanner={inEmbeddedApp()}
|
||||||
/>
|
/>
|
||||||
) : null,
|
) : null,
|
||||||
this.state.author ? (
|
this.state.author ? (
|
||||||
<ProfilePagesContent key="ProfilePagesContent">
|
<ProfilePagesContent key="ProfilePagesContent">
|
||||||
<MediaListWrapper
|
<MediaListWrapper title={this.state.title} className="items-list-ver">
|
||||||
title={this.state.title}
|
<ProfileMediaFilters
|
||||||
className="items-list-ver"
|
hidden={this.state.hiddenFilters}
|
||||||
>
|
tags={this.state.availableTags}
|
||||||
<ProfileMediaFilters hidden={this.state.hiddenFilters} tags={this.state.availableTags} onFiltersUpdate={this.onFiltersUpdate} />
|
onFiltersUpdate={this.onFiltersUpdate}
|
||||||
<ProfileMediaTags hidden={this.state.hiddenTags} tags={this.state.availableTags} onTagSelect={this.onTagSelect} />
|
/>
|
||||||
|
<ProfileMediaTags
|
||||||
|
hidden={this.state.hiddenTags}
|
||||||
|
tags={this.state.availableTags}
|
||||||
|
onTagSelect={this.onTagSelect}
|
||||||
|
/>
|
||||||
<ProfileMediaSorting hidden={this.state.hiddenSorting} onSortSelect={this.onSortSelect} />
|
<ProfileMediaSorting hidden={this.state.hiddenSorting} onSortSelect={this.onSortSelect} />
|
||||||
<LazyLoadItemListAsync
|
<LazyLoadItemListAsync
|
||||||
key={this.state.requestUrl}
|
key={this.state.requestUrl}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { PageStore, MediaPageStore } from '../utils/stores/';
|
import { PageStore, MediaPageStore } from '../utils/stores/';
|
||||||
import { MediaPageActions } from '../utils/actions/';
|
import { MediaPageActions } from '../utils/actions/';
|
||||||
|
import { inEmbeddedApp } from '../utils/helpers/';
|
||||||
import ViewerError from '../components/media-page/ViewerError';
|
import ViewerError from '../components/media-page/ViewerError';
|
||||||
import ViewerInfo from '../components/media-page/ViewerInfo';
|
import ViewerInfo from '../components/media-page/ViewerInfo';
|
||||||
import ViewerSidebar from '../components/media-page/ViewerSidebar';
|
import ViewerSidebar from '../components/media-page/ViewerSidebar';
|
||||||
@ -86,7 +87,7 @@ export class _MediaPage extends Page {
|
|||||||
{!this.state.infoAndSidebarViewType
|
{!this.state.infoAndSidebarViewType
|
||||||
? [
|
? [
|
||||||
<ViewerInfo key="viewer-info" />,
|
<ViewerInfo key="viewer-info" />,
|
||||||
this.state.pagePlaylistLoaded ? (
|
!inEmbeddedApp() && this.state.pagePlaylistLoaded ? (
|
||||||
<ViewerSidebar
|
<ViewerSidebar
|
||||||
key="viewer-sidebar"
|
key="viewer-sidebar"
|
||||||
mediaId={MediaPageStore.get('media-id')}
|
mediaId={MediaPageStore.get('media-id')}
|
||||||
@ -95,7 +96,7 @@ export class _MediaPage extends Page {
|
|||||||
) : null,
|
) : null,
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
this.state.pagePlaylistLoaded ? (
|
!inEmbeddedApp() && this.state.pagePlaylistLoaded ? (
|
||||||
<ViewerSidebar
|
<ViewerSidebar
|
||||||
key="viewer-sidebar"
|
key="viewer-sidebar"
|
||||||
mediaId={MediaPageStore.get('media-id')}
|
mediaId={MediaPageStore.get('media-id')}
|
||||||
|
|||||||
@ -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.
|
// 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 { PageStore, MediaPageStore, VideoViewerStore } from '../utils/stores/';
|
||||||
import { MediaPageActions } from '../utils/actions/';
|
import { MediaPageActions } from '../utils/actions/';
|
||||||
|
import { inEmbeddedApp } from '../utils/helpers/';
|
||||||
import ViewerInfoVideo from '../components/media-page/ViewerInfoVideo';
|
import ViewerInfoVideo from '../components/media-page/ViewerInfoVideo';
|
||||||
import ViewerError from '../components/media-page/ViewerError';
|
import ViewerError from '../components/media-page/ViewerError';
|
||||||
import ViewerSidebar from '../components/media-page/ViewerSidebar';
|
import ViewerSidebar from '../components/media-page/ViewerSidebar';
|
||||||
@ -54,7 +55,8 @@ export class _VideoMediaPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMediaLoad() {
|
onMediaLoad() {
|
||||||
const isVideoMedia = 'video' === MediaPageStore.get('media-type') || 'audio' === MediaPageStore.get('media-type');
|
const isVideoMedia =
|
||||||
|
'video' === MediaPageStore.get('media-type') || 'audio' === MediaPageStore.get('media-type');
|
||||||
|
|
||||||
if (isVideoMedia) {
|
if (isVideoMedia) {
|
||||||
this.onViewerModeChange = this.onViewerModeChange.bind(this);
|
this.onViewerModeChange = this.onViewerModeChange.bind(this);
|
||||||
@ -102,7 +104,7 @@ export class _VideoMediaPage extends Page {
|
|||||||
{!this.state.wideLayout || (this.state.isVideoMedia && this.state.theaterMode)
|
{!this.state.wideLayout || (this.state.isVideoMedia && this.state.theaterMode)
|
||||||
? [
|
? [
|
||||||
<ViewerInfoVideo key="viewer-info" />,
|
<ViewerInfoVideo key="viewer-info" />,
|
||||||
this.state.pagePlaylistLoaded ? (
|
!inEmbeddedApp() && this.state.pagePlaylistLoaded ? (
|
||||||
<ViewerSidebar
|
<ViewerSidebar
|
||||||
key="viewer-sidebar"
|
key="viewer-sidebar"
|
||||||
mediaId={MediaPageStore.get('media-id')}
|
mediaId={MediaPageStore.get('media-id')}
|
||||||
@ -111,7 +113,7 @@ export class _VideoMediaPage extends Page {
|
|||||||
) : null,
|
) : null,
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
this.state.pagePlaylistLoaded ? (
|
!inEmbeddedApp() && this.state.pagePlaylistLoaded ? (
|
||||||
<ViewerSidebar
|
<ViewerSidebar
|
||||||
key="viewer-sidebar"
|
key="viewer-sidebar"
|
||||||
mediaId={MediaPageStore.get('media-id')}
|
mediaId={MediaPageStore.get('media-id')}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { BrowserCache } from '../classes/';
|
import { BrowserCache } from '../classes/';
|
||||||
import { PageStore } from '../stores/';
|
import { PageStore } from '../stores/';
|
||||||
import { addClassname, removeClassname } from '../helpers/';
|
import { addClassname, removeClassname, inEmbeddedApp } from '../helpers/';
|
||||||
import SiteContext from './SiteContext';
|
import SiteContext from './SiteContext';
|
||||||
|
|
||||||
let slidingSidebarTimeout;
|
let slidingSidebarTimeout;
|
||||||
@ -45,7 +45,10 @@ export const LayoutProvider = ({ children }) => {
|
|||||||
const site = useContext(SiteContext);
|
const site = useContext(SiteContext);
|
||||||
const cache = new BrowserCache('MediaCMS[' + site.id + '][layout]', 86400);
|
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 enabledSidebar = Boolean(document.getElementById('app-sidebar') || document.querySelector('.page-sidebar'));
|
||||||
|
|
||||||
const [visibleSidebar, setVisibleSidebar] = useState(cache.get('visible-sidebar'));
|
const [visibleSidebar, setVisibleSidebar] = useState(cache.get('visible-sidebar'));
|
||||||
const [visibleMobileSearch, setVisibleMobileSearch] = useState(false);
|
const [visibleMobileSearch, setVisibleMobileSearch] = useState(false);
|
||||||
@ -61,28 +64,27 @@ export const LayoutProvider = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visibleSidebar) {
|
if (!isEmbeddedApp && visibleSidebar) {
|
||||||
addClassname(document.body, 'visible-sidebar');
|
addClassname(document.body, 'visible-sidebar');
|
||||||
} else {
|
} else {
|
||||||
removeClassname(document.body, 'visible-sidebar');
|
removeClassname(document.body, 'visible-sidebar');
|
||||||
}
|
}
|
||||||
if ('media' !== PageStore.get('current-page') && 1023 < window.innerWidth) {
|
|
||||||
|
if (!isEmbeddedApp && !isMediaPage && 1023 < window.innerWidth) {
|
||||||
cache.set('visible-sidebar', visibleSidebar);
|
cache.set('visible-sidebar', visibleSidebar);
|
||||||
}
|
}
|
||||||
}, [visibleSidebar]);
|
}, [isEmbeddedApp, isMediaPage, visibleSidebar]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
PageStore.once('page_init', () => {
|
PageStore.once('page_init', () => {
|
||||||
if ('media' === PageStore.get('current-page')) {
|
if (isEmbeddedApp || isMediaPage) {
|
||||||
setVisibleSidebar(false);
|
setVisibleSidebar(false);
|
||||||
removeClassname(document.body, 'visible-sidebar');
|
removeClassname(document.body, 'visible-sidebar');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setVisibleSidebar(
|
setVisibleSidebar(
|
||||||
'media' !== PageStore.get('current-page') &&
|
!isEmbeddedApp && !isMediaPage && 1023 < window.innerWidth && (null === visibleSidebar || visibleSidebar)
|
||||||
1023 < window.innerWidth &&
|
|
||||||
(null === visibleSidebar || visibleSidebar)
|
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
20
frontend/src/static/js/utils/helpers/embeddedApp.ts
Normal file
20
frontend/src/static/js/utils/helpers/embeddedApp.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export function inEmbeddedApp() {
|
||||||
|
try {
|
||||||
|
const params = new URL(globalThis.location.href).searchParams;
|
||||||
|
const mode = params.get('mode');
|
||||||
|
|
||||||
|
if (mode === 'embed_mode') {
|
||||||
|
sessionStorage.setItem('media_cms_embed_mode', 'true');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'standard') {
|
||||||
|
sessionStorage.removeItem('media_cms_embed_mode');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionStorage.getItem('media_cms_embed_mode') === 'true';
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,3 +14,4 @@ export * from './quickSort';
|
|||||||
export * from './requests';
|
export * from './requests';
|
||||||
export { translateString } from './translate';
|
export { translateString } from './translate';
|
||||||
export { replaceString } from './replacementStrings';
|
export { replaceString } from './replacementStrings';
|
||||||
|
export * from './embeddedApp';
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
|
|||||||
import { ThemeProvider } from './contexts/ThemeContext';
|
import { ThemeProvider } from './contexts/ThemeContext';
|
||||||
import { LayoutProvider } from './contexts/LayoutContext';
|
import { LayoutProvider } from './contexts/LayoutContext';
|
||||||
import { UserProvider } from './contexts/UserContext';
|
import { UserProvider } from './contexts/UserContext';
|
||||||
|
import { inEmbeddedApp } from './helpers';
|
||||||
|
|
||||||
const AppProviders = ({ children }) => (
|
const AppProviders = ({ children }) => (
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
@ -15,9 +16,27 @@ const AppProviders = ({ children }) => (
|
|||||||
import { PageHeader, PageSidebar } from '../components/page-layout';
|
import { PageHeader, PageSidebar } from '../components/page-layout';
|
||||||
|
|
||||||
export function renderPage(idSelector, PageComponent) {
|
export function renderPage(idSelector, PageComponent) {
|
||||||
|
if (inEmbeddedApp()) {
|
||||||
|
globalThis.document.body.classList.add('embedded-app');
|
||||||
|
globalThis.document.body.classList.remove('visible-sidebar');
|
||||||
|
|
||||||
|
const appContent = idSelector ? document.getElementById(idSelector) : undefined;
|
||||||
|
|
||||||
|
if (appContent && PageComponent) {
|
||||||
|
ReactDOM.render(
|
||||||
|
<AppProviders>
|
||||||
|
<PageComponent />
|
||||||
|
</AppProviders>,
|
||||||
|
appContent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appContent = idSelector ? document.getElementById(idSelector) : undefined;
|
||||||
const appHeader = document.getElementById('app-header');
|
const appHeader = document.getElementById('app-header');
|
||||||
const appSidebar = document.getElementById('app-sidebar');
|
const appSidebar = document.getElementById('app-sidebar');
|
||||||
const appContent = idSelector ? document.getElementById(idSelector) : undefined;
|
|
||||||
|
|
||||||
if (appContent && PageComponent) {
|
if (appContent && PageComponent) {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
!function(){"use strict";var n,e={4256:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every(function(n){return t.O[n](r[c])})?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=594,function(){var n={594:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some(function(e){return 0!==n[e]})){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],function(){return t(4256)});o=t.O(o)}();
|
!function(){"use strict";var n,e={4256:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every((function(n){return t.O[n](r[c])}))?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=594,function(){var n={594:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some((function(e){return 0!==n[e]}))){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],(function(){return t(4256)}));o=t.O(o)}();
|
||||||
@ -1 +1 @@
|
|||||||
!function(){"use strict";var n,e={5879:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every(function(n){return t.O[n](r[c])})?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=543,function(){var n={543:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some(function(e){return 0!==n[e]})){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],function(){return t(5879)});o=t.O(o)}();
|
!function(){"use strict";var n,e={5879:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every((function(n){return t.O[n](r[c])}))?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=543,function(){var n={543:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some((function(e){return 0!==n[e]}))){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],(function(){return t(5879)}));o=t.O(o)}();
|
||||||
@ -1 +1 @@
|
|||||||
!function(){"use strict";var n,e={1684:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every(function(n){return t.O[n](r[c])})?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=152,function(){var n={152:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some(function(e){return 0!==n[e]})){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],function(){return t(1684)});o=t.O(o)}();
|
!function(){"use strict";var n,e={1684:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every((function(n){return t.O[n](r[c])}))?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=152,function(){var n={152:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some((function(e){return 0!==n[e]}))){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],(function(){return t(1684)}));o=t.O(o)}();
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user