This commit is contained in:
Markos Gogoulos
2026-02-01 13:21:39 +02:00
parent a77761ec35
commit ba53467033
9 changed files with 218 additions and 63 deletions

View File

@@ -1 +1 @@
VERSION = "8"
VERSION = "8.07"

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { translateString } from '../utils/helpers/';
import { translateString, inEmbeddedApp } from '../utils/helpers/';
interface MediaListHeaderProps {
title?: string;
@@ -11,10 +11,12 @@ interface MediaListHeaderProps {
export const MediaListHeader: React.FC<MediaListHeaderProps> = (props) => {
const viewAllText = props.viewAllText || translateString('VIEW ALL');
const isEmbedMode = inEmbeddedApp();
return (
<div className={(props.className ? props.className + ' ' : '') + 'media-list-header'} style={props.style}>
<h2>{props.title}</h2>
{props.viewAllLink ? (
{!isEmbedMode && props.viewAllLink ? (
<h3>
{' '}
<a href={props.viewAllLink} title={viewAllText}>

View File

@@ -1,18 +1,50 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useMediaItem } from '../../utils/hooks/';
import { PositiveInteger, PositiveIntegerOrZero } from '../../utils/helpers/';
import { PositiveInteger, PositiveIntegerOrZero, inEmbeddedApp } from '../../utils/helpers/';
import { MediaItemThumbnailLink, itemClassname } from './includes/items/';
import { Item } from './Item';
export function MediaItem(props) {
const type = props.type;
const isEmbedMode = inEmbeddedApp();
const [titleComponent, descriptionComponent, thumbnailUrl, UnderThumbWrapper, editMediaComponent, metaComponents, viewMediaComponent] =
const [titleComponentOrig, descriptionComponent, thumbnailUrl, UnderThumbWrapperOrig, editMediaComponent, metaComponents, viewMediaComponent] =
useMediaItem({ ...props, type });
// In embed mode, override components to remove links
const ItemTitle = ({ title }) => (
<h3>
<span>{title}</span>
</h3>
);
const ItemMain = ({ children }) => <div className="item-main">{children}</div>;
const titleComponent = isEmbedMode
? () => <ItemTitle title={props.title} />
: titleComponentOrig;
const UnderThumbWrapper = isEmbedMode ? ItemMain : UnderThumbWrapperOrig;
function thumbnailComponent() {
if (isEmbedMode) {
// In embed mode, render thumbnail without link
const thumbStyle = thumbnailUrl ? { backgroundImage: "url('" + thumbnailUrl + "')" } : null;
return (
<div
key="item-thumb"
className={'item-thumb' + (!thumbnailUrl ? ' no-thumb' : '')}
style={thumbStyle}
>
{thumbnailUrl ? (
<div key="item-type-icon" className="item-type-icon">
<div></div>
</div>
) : null}
</div>
);
}
return <MediaItemThumbnailLink src={thumbnailUrl} title={props.title} link={props.link} />;
}
@@ -23,13 +55,15 @@ export function MediaItem(props) {
);
const finalClassname = containerClassname +
(props.showSelection ? ' with-selection' : '') +
(props.showSelection && !isEmbedMode ? ' with-selection' : '') +
(props.isSelected ? ' selected' : '') +
(props.hasAnySelection ? ' has-any-selection' : '');
(props.hasAnySelection || isEmbedMode ? ' has-any-selection' : '');
const handleItemClick = (e) => {
// If there's any selection active, clicking the item should toggle selection
if (props.hasAnySelection && props.onCheckboxChange) {
const isEmbedMode = inEmbeddedApp();
// In embed mode or if there's any selection active, clicking the item should toggle selection
if ((isEmbedMode || props.hasAnySelection) && props.onCheckboxChange) {
// Check if clicking on the checkbox itself, edit icon, or view icon
if (e.target.closest('.item-selection-checkbox') ||
e.target.closest('.item-edit-icon') ||
@@ -59,16 +93,24 @@ export function MediaItem(props) {
</div>
)}
{editMediaComponent()}
{viewMediaComponent()}
{!isEmbedMode && editMediaComponent()}
{!isEmbedMode && viewMediaComponent()}
{thumbnailComponent()}
{isEmbedMode ? (
<UnderThumbWrapper>
{titleComponent()}
{metaComponents()}
{descriptionComponent()}
</UnderThumbWrapper>
) : (
<UnderThumbWrapper title={props.title} link={props.link}>
{titleComponent()}
{metaComponents()}
{descriptionComponent()}
</UnderThumbWrapper>
)}
</div>
</div>
);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useMediaItem } from '../../utils/hooks/';
import { PositiveIntegerOrZero } from '../../utils/helpers/';
import { PositiveIntegerOrZero, inEmbeddedApp } from '../../utils/helpers/';
import { MediaDurationInfo } from '../../utils/classes/';
import { MediaPlaylistOptions } from '../media-playlist-options/MediaPlaylistOptions';
import { MediaItemDuration, MediaItemPlaylistIndex, itemClassname } from './includes/items/';
@@ -9,10 +9,26 @@ import { MediaItem } from './MediaItem';
export function MediaItemAudio(props) {
const type = props.type;
const isEmbedMode = inEmbeddedApp();
const [titleComponent, descriptionComponent, thumbnailUrl, UnderThumbWrapper, editMediaComponent, metaComponents, viewMediaComponent] =
const [titleComponentOrig, descriptionComponent, thumbnailUrl, UnderThumbWrapperOrig, editMediaComponent, metaComponents, viewMediaComponent] =
useMediaItem({ ...props, type });
// In embed mode, override components to remove links
const ItemTitle = ({ title }) => (
<h3>
<span>{title}</span>
</h3>
);
const ItemMain = ({ children }) => <div className="item-main">{children}</div>;
const titleComponent = isEmbedMode
? () => <ItemTitle title={props.title} />
: titleComponentOrig;
const UnderThumbWrapper = isEmbedMode ? ItemMain : UnderThumbWrapperOrig;
const _MediaDurationInfo = new MediaDurationInfo();
_MediaDurationInfo.update(props.duration);
@@ -22,6 +38,21 @@ export function MediaItemAudio(props) {
const durationISO8601 = _MediaDurationInfo.ISO8601();
function thumbnailComponent() {
if (isEmbedMode) {
// In embed mode, render thumbnail without link
return (
<div
key="item-thumb"
className={'item-thumb' + (!thumbnailUrl ? ' no-thumb' : '')}
style={!thumbnailUrl ? null : { backgroundImage: "url('" + thumbnailUrl + "')" }}
>
{props.inPlaylistView ? null : (
<MediaItemDuration ariaLabel={duration} time={durationISO8601} text={durationStr} />
)}
</div>
);
}
const attr = {
key: 'item-thumb',
href: props.link,
@@ -66,13 +97,13 @@ export function MediaItemAudio(props) {
);
const finalClassname = containerClassname +
(props.showSelection ? ' with-selection' : '') +
(props.showSelection && !isEmbedMode ? ' with-selection' : '') +
(props.isSelected ? ' selected' : '') +
(props.hasAnySelection ? ' has-any-selection' : '');
(props.hasAnySelection || isEmbedMode ? ' has-any-selection' : '');
const handleItemClick = (e) => {
// If there's any selection active, clicking the item should toggle selection
if (props.hasAnySelection && props.onCheckboxChange) {
// In embed mode or if there's any selection active, clicking the item should toggle selection
if ((isEmbedMode || props.hasAnySelection) && props.onCheckboxChange) {
// Check if clicking on the checkbox itself, edit icon, or view icon
if (e.target.closest('.item-selection-checkbox') ||
e.target.closest('.item-edit-icon') ||
@@ -104,16 +135,24 @@ export function MediaItemAudio(props) {
</div>
)}
{editMediaComponent()}
{viewMediaComponent()}
{!isEmbedMode && editMediaComponent()}
{!isEmbedMode && viewMediaComponent()}
{thumbnailComponent()}
{isEmbedMode ? (
<UnderThumbWrapper>
{titleComponent()}
{metaComponents()}
{descriptionComponent()}
</UnderThumbWrapper>
) : (
<UnderThumbWrapper title={props.title} link={props.link}>
{titleComponent()}
{metaComponents()}
{descriptionComponent()}
</UnderThumbWrapper>
)}
{playlistOptionsComponent()}
</div>

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useMediaItem } from '../../utils/hooks/';
import { PositiveIntegerOrZero } from '../../utils/helpers/';
import { PositiveIntegerOrZero, inEmbeddedApp } from '../../utils/helpers/';
import { MediaDurationInfo } from '../../utils/classes/';
import { MediaPlaylistOptions } from '../media-playlist-options/MediaPlaylistOptions.jsx';
import { MediaItemVideoPlayer, MediaItemDuration, MediaItemVideoPreviewer, MediaItemPlaylistIndex, itemClassname } from './includes/items/';
@@ -9,10 +9,26 @@ import { MediaItem } from './MediaItem';
export function MediaItemVideo(props) {
const type = props.type;
const isEmbedMode = inEmbeddedApp();
const [titleComponent, descriptionComponent, thumbnailUrl, UnderThumbWrapper, editMediaComponent, metaComponents, viewMediaComponent] =
const [titleComponentOrig, descriptionComponent, thumbnailUrl, UnderThumbWrapperOrig, editMediaComponent, metaComponents, viewMediaComponent] =
useMediaItem({ ...props, type });
// In embed mode, override components to remove links
const ItemTitle = ({ title }) => (
<h3>
<span>{title}</span>
</h3>
);
const ItemMain = ({ children }) => <div className="item-main">{children}</div>;
const titleComponent = isEmbedMode
? () => <ItemTitle title={props.title} />
: titleComponentOrig;
const UnderThumbWrapper = isEmbedMode ? ItemMain : UnderThumbWrapperOrig;
const _MediaDurationInfo = new MediaDurationInfo();
_MediaDurationInfo.update(props.duration);
@@ -26,6 +42,24 @@ export function MediaItemVideo(props) {
}
function thumbnailComponent() {
if (isEmbedMode) {
// In embed mode, render thumbnail without link
return (
<div
key="item-thumb"
className={'item-thumb' + (!thumbnailUrl ? ' no-thumb' : '')}
style={!thumbnailUrl ? null : { backgroundImage: "url('" + thumbnailUrl + "')" }}
>
{props.inPlaylistView ? null : (
<MediaItemDuration ariaLabel={duration} time={durationISO8601} text={durationStr} />
)}
{props.inPlaylistView || props.inPlaylistPage ? null : (
<MediaItemVideoPreviewer url={props.preview_thumbnail} />
)}
</div>
);
}
const attr = {
key: 'item-thumb',
href: props.link,
@@ -73,13 +107,13 @@ export function MediaItemVideo(props) {
);
const finalClassname = containerClassname +
(props.showSelection ? ' with-selection' : '') +
(props.showSelection && !isEmbedMode ? ' with-selection' : '') +
(props.isSelected ? ' selected' : '') +
(props.hasAnySelection ? ' has-any-selection' : '');
(props.hasAnySelection || isEmbedMode ? ' has-any-selection' : '');
const handleItemClick = (e) => {
// If there's any selection active, clicking the item should toggle selection
if (props.hasAnySelection && props.onCheckboxChange) {
// In embed mode or if there's any selection active, clicking the item should toggle selection
if ((isEmbedMode || props.hasAnySelection) && props.onCheckboxChange) {
// Check if clicking on the checkbox itself, edit icon, or view icon
if (e.target.closest('.item-selection-checkbox') ||
e.target.closest('.item-edit-icon') ||
@@ -111,20 +145,28 @@ export function MediaItemVideo(props) {
</div>
)}
{editMediaComponent()}
{viewMediaComponent()}
{!isEmbedMode && editMediaComponent()}
{!isEmbedMode && viewMediaComponent()}
{props.hasMediaViewer ? videoViewerComponent() : thumbnailComponent()}
{isEmbedMode ? (
<UnderThumbWrapper>
{titleComponent()}
{metaComponents()}
{descriptionComponent()}
</UnderThumbWrapper>
) : (
<UnderThumbWrapper title={props.title} link={props.link}>
{titleComponent()}
{metaComponents()}
{descriptionComponent()}
</UnderThumbWrapper>
</div>
)}
{playlistOptionsComponent()}
</div>
</div>
);
}

View File

@@ -5,7 +5,7 @@ import { LinksContext, MemberContext, SiteContext } from '../../utils/contexts/'
import { PageStore, ProfilePageStore } from '../../utils/stores/';
import { PageActions, ProfilePageActions } from '../../utils/actions/';
import { CircleIconButton, PopupMain } from '../_shared';
import { translateString } from '../../utils/helpers/';
import { translateString, inEmbeddedApp } from '../../utils/helpers/';
class ProfileSearchBar extends React.PureComponent {
constructor(props) {
@@ -372,18 +372,22 @@ class NavMenuInlineTabs extends React.PureComponent {
}
render() {
const isEmbedMode = inEmbeddedApp();
return (
<nav ref="tabsNav" className="profile-nav items-list-outer list-inline list-slider">
<div className="profile-nav-inner items-list-outer">
{this.state.displayPrev ? this.previousBtn : null}
<ul className="items-list-wrap" ref="itemsListWrap">
{!isEmbedMode ? (
<InlineTab
id="about"
isActive={'about' === this.props.type}
label={translateString('About')}
link={LinksContext._currentValue.profile.about}
/>
) : null}
<InlineTab
id="media"
isActive={'media' === this.props.type}
@@ -407,7 +411,7 @@ class NavMenuInlineTabs extends React.PureComponent {
/>
) : null}
{MemberContext._currentValue.can.saveMedia ? (
{!isEmbedMode && MemberContext._currentValue.can.saveMedia ? (
<InlineTab
id="playlists"
isActive={'playlists' === this.props.type}
@@ -768,7 +772,15 @@ export default function ProfilePagesHeader(props) {
)}
<div className="profile-info-nav-wrap">
{props.author.thumbnail_url || props.author.name ? (
{inEmbeddedApp() ? (
<div className="profile-info">
<div className="profile-info-inner">
<div>
<h1>{translateString('Embed Media')}</h1>
</div>
</div>
</div>
) : props.author.thumbnail_url || props.author.name ? (
<div className="profile-info">
<div className="profile-info-inner">
<div>

View File

@@ -202,13 +202,29 @@ export class ProfileMediaPage extends Page {
}
handleMediaSelection(mediaId, isSelected) {
const isEmbedMode = inEmbeddedApp();
this.setState((prevState) => {
const newSelectedMedia = new Set(prevState.selectedMedia);
const newSelectedMedia = new Set();
// In embed mode, only allow single selection
if (isEmbedMode) {
if (isSelected) {
newSelectedMedia.add(mediaId);
console.log('Selected media item:', mediaId);
}
} else {
// Normal mode: allow multiple selection
newSelectedMedia.clear();
prevState.selectedMedia.forEach((id) => newSelectedMedia.add(id));
if (isSelected) {
newSelectedMedia.add(mediaId);
} else {
newSelectedMedia.delete(mediaId);
}
}
return { selectedMedia: newSelectedMedia };
});
}
@@ -917,6 +933,7 @@ export class ProfileMediaPage extends Page {
const authorData = ProfilePageStore.get('author-data');
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
const isEmbedMode = inEmbeddedApp();
// Check if any filters are active (excluding default sort and tags)
const hasActiveFilters =
@@ -948,15 +965,16 @@ export class ProfileMediaPage extends Page {
this.state.author ? (
<ProfilePagesContent key="ProfilePagesContent">
<MediaListWrapper
title={this.state.title}
title={isEmbedMode ? undefined : this.state.title}
className="items-list-ver"
showBulkActions={isMediaAuthor}
style={isEmbedMode ? { marginTop: '24px' } : undefined}
showBulkActions={!isEmbedMode && isMediaAuthor}
selectedCount={this.state.selectedMedia.size}
totalCount={this.state.availableMediaIds.length}
onBulkAction={this.handleBulkAction}
onSelectAll={this.handleSelectAll}
onDeselectAll={this.handleDeselectAll}
showAddMediaButton={isMediaAuthor}
showAddMediaButton={!isEmbedMode && isMediaAuthor}
>
<ProfileMediaFilters
hidden={this.state.hiddenFilters}
@@ -979,7 +997,7 @@ export class ProfileMediaPage extends Page {
hideViews={!PageStore.get('config-media-item').displayViews}
hideDate={!PageStore.get('config-media-item').displayPublishDate}
canEdit={isMediaAuthor}
showSelection={isMediaAuthor}
showSelection={isMediaAuthor || isEmbedMode}
hasAnySelection={this.state.selectedMedia.size > 0}
selectedMedia={this.state.selectedMedia}
onMediaSelection={this.handleMediaSelection}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long