Compare commits

...

2 Commits

Author SHA1 Message Date
Markos Gogoulos
7324a0def7 fix 2025-10-26 18:28:07 +02:00
Markos Gogoulos
675b6eb977 fix 2025-10-26 17:40:13 +02:00
8 changed files with 893 additions and 1070 deletions

View File

@ -1 +1 @@
VERSION = "7.3.0" VERSION = "11.555.0"

View File

@ -200,6 +200,18 @@ class MediaList(APIView):
media = self._get_media_queryset(request) media = self._get_media_queryset(request)
already_sorted = True already_sorted = True
if query:
query = helpers.clean_query(query)
q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS]
if q_parts:
query = SearchQuery(q_parts[0] + ":*", search_type="raw")
for part in q_parts[1:]:
query &= SearchQuery(part + ":*", search_type="raw")
else:
query = None
if query:
media = media.filter(search=query)
if tag: if tag:
media = media.filter(tags__title=tag) media = media.filter(tags__title=tag)
@ -222,9 +234,6 @@ class MediaList(APIView):
if publish_state and publish_state in ['private', 'public', 'unlisted']: if publish_state and publish_state in ['private', 'public', 'unlisted']:
media = media.filter(state=publish_state) media = media.filter(state=publish_state)
if query:
media = media.filter(title__icontains=query)
if not already_sorted: if not already_sorted:
media = media.order_by(f"{ordering}{sort_by}") media = media.order_by(f"{ordering}{sort_by}")

View File

@ -467,6 +467,9 @@
max-width: 100%; max-width: 100%;
margin: 0 auto; margin: 0 auto;
clear: both; clear: both;
display: flex;
align-items: center;
position: relative;
.sliding-sidebar & { .sliding-sidebar & {
transition-property: width; transition-property: width;
@ -474,44 +477,54 @@
} }
} }
&.items-list-outer .previous-slide, .previous-slide,
&.items-list-outer .next-slide { .next-slide {
top: 4px; position: relative;
bottom: 4px; display: inline-flex;
padding: 0 !important; align-items: center;
justify-content: center;
padding: 0 4px !important;
margin: 0; margin: 0;
background-color: var(--profile-page-header-bg-color); background-color: var(--profile-page-header-bg-color);
height: $_authorPage-navHeight;
flex-shrink: 0;
z-index: 2;
.circle-icon-button { .circle-icon-button {
margin: 0; margin: 0;
background-color: var(--profile-page-header-bg-color); background-color: transparent;
} }
} }
&.items-list-outer .previous-slide { .previous-slide {
left: -0.75em; padding-right: 8px !important;
left: -1px;
} }
&.items-list-outer .next-slide { .next-slide {
right: -0.75em; padding-left: 8px !important;
right: -1px;
} }
ul { ul {
position: relative; position: relative;
width: 100%; flex: 1;
float: left;
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
display: flex;
flex-wrap: nowrap;
align-items: center;
font-size: 0;
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
min-width: 0; // Allow flex item to shrink
// Remove whitespace between inline-block elements on mobile // Hide scrollbar but keep functionality
@media screen and (max-width: 768px) { scrollbar-width: none; // Firefox
font-size: 0; -ms-overflow-style: none; // IE/Edge
display: flex;
flex-wrap: nowrap; &::-webkit-scrollbar {
overflow-x: auto; display: none; // Chrome/Safari
} }
li { li {
@ -519,10 +532,7 @@
display: inline-block; display: inline-block;
text-align: center; text-align: center;
vertical-align: bottom; vertical-align: bottom;
flex-shrink: 0;
@media screen and (max-width: 768px) {
flex-shrink: 0;
}
a { a {
display: block; display: block;
@ -591,36 +601,54 @@
} }
&.media-search { &.media-search {
display: flex !important;
align-items: center;
> * { > * {
position: relative; position: relative;
display: table; display: flex;
float: left; align-items: center;
width: auto; width: auto;
height: 3rem; height: 3rem;
> span { > span {
display: table-cell; display: inline-flex;
vertical-align: middle; align-items: center;
} }
} }
form {
display: flex;
align-items: center;
}
button { button {
background-color: transparent; background-color: transparent;
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
} }
input[type='text'] { input[type='text'] {
width: 178px;
max-width: 178px; max-width: 178px;
padding-left: 0; padding-left: 8px;
padding-right: 0; padding-right: 8px;
font-weight: 500; font-weight: 500;
border-width: 0 0 2px; border-width: 0 0 2px;
border-color: var(--profile-page-nav-link-text-color);
background-color: transparent; background-color: transparent;
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
box-shadow: none; box-shadow: none;
font-size: 14px;
color: var(--profile-page-nav-link-text-color);
&::placeholder {
color: var(--profile-page-nav-link-text-color);
opacity: 0.7;
}
&:focus { &:focus {
border-bottom-color: var(--profile-page-nav-link-active-after-bg-color); border-bottom-color: var(--profile-page-nav-link-active-after-bg-color);
outline: none;
} }
} }
} }

View File

@ -5,7 +5,6 @@ import { LinksContext, MemberContext, SiteContext } from '../../utils/contexts/'
import { PageStore, ProfilePageStore } from '../../utils/stores/'; import { PageStore, ProfilePageStore } from '../../utils/stores/';
import { PageActions, ProfilePageActions } from '../../utils/actions/'; import { PageActions, ProfilePageActions } from '../../utils/actions/';
import { CircleIconButton, PopupMain } from '../_shared'; import { CircleIconButton, PopupMain } from '../_shared';
import ItemsInlineSlider from '../item-list/includes/itemLists/ItemsInlineSlider';
import { translateString } from '../../utils/helpers/'; import { translateString } from '../../utils/helpers/';
class ProfileSearchBar extends React.PureComponent { class ProfileSearchBar extends React.PureComponent {
@ -26,6 +25,7 @@ class ProfileSearchBar extends React.PureComponent {
this.updateTimeout = null; this.updateTimeout = null;
this.pendingUpdate = false; this.pendingUpdate = false;
this.justShown = false;
} }
updateQuery(value) { updateQuery(value) {
@ -104,10 +104,15 @@ class ProfileSearchBar extends React.PureComponent {
} }
onInputBlur() { onInputBlur() {
// Don't hide immediately after showing to prevent race condition
if (this.justShown) {
return;
}
this.hideForm(); this.hideForm();
} }
showForm() { showForm() {
this.justShown = true;
this.setState( this.setState(
{ {
visibleForm: true, visibleForm: true,
@ -116,6 +121,10 @@ class ProfileSearchBar extends React.PureComponent {
if ('function' === typeof this.props.toggleSearchField) { if ('function' === typeof this.props.toggleSearchField) {
this.props.toggleSearchField(); this.props.toggleSearchField();
} }
// Reset the flag after a short delay
setTimeout(() => {
this.justShown = false;
}, 200);
} }
); );
} }
@ -244,12 +253,11 @@ class NavMenuInlineTabs extends React.PureComponent {
displayPrev: false, displayPrev: false,
}; };
this.inlineSlider = null;
this.nextSlide = this.nextSlide.bind(this); this.nextSlide = this.nextSlide.bind(this);
this.prevSlide = this.prevSlide.bind(this); this.prevSlide = this.prevSlide.bind(this);
this.updateSlider = this.updateSlider.bind(this, false); this.updateSlider = this.updateSlider.bind(this);
this.updateSliderButtonsView = this.updateSliderButtonsView.bind(this);
this.onToggleSearchField = this.onToggleSearchField.bind(this); this.onToggleSearchField = this.onToggleSearchField.bind(this);
@ -303,44 +311,57 @@ class NavMenuInlineTabs extends React.PureComponent {
componentDidMount() { componentDidMount() {
this.updateSlider(); this.updateSlider();
if (this.refs.itemsListWrap) {
this.refs.itemsListWrap.addEventListener('scroll', this.updateSliderButtonsView.bind(this));
}
}
componentWillUnmount() {
if (this.refs.itemsListWrap) {
this.refs.itemsListWrap.removeEventListener('scroll', this.updateSliderButtonsView.bind(this));
}
} }
nextSlide() { nextSlide() {
this.inlineSlider.nextSlide(); if (!this.refs.itemsListWrap) return;
this.updateSliderButtonsView(); const scrollAmount = this.refs.itemsListWrap.offsetWidth * 0.7; // Scroll 70% of visible width
this.inlineSlider.scrollToCurrentSlide(); this.refs.itemsListWrap.scrollLeft += scrollAmount;
setTimeout(() => this.updateSliderButtonsView(), 50);
} }
prevSlide() { prevSlide() {
this.inlineSlider.previousSlide(); if (!this.refs.itemsListWrap) return;
this.updateSliderButtonsView(); const scrollAmount = this.refs.itemsListWrap.offsetWidth * 0.7; // Scroll 70% of visible width
this.inlineSlider.scrollToCurrentSlide(); this.refs.itemsListWrap.scrollLeft -= scrollAmount;
setTimeout(() => this.updateSliderButtonsView(), 50);
} }
updateSlider(afterItemsUpdate) { updateSlider(afterItemsUpdate) {
if (!this.inlineSlider) {
this.inlineSlider = new ItemsInlineSlider(this.refs.itemsListWrap, '.profile-nav ul li');
}
this.inlineSlider.updateDataState(document.querySelectorAll('.profile-nav ul li').length, true, !afterItemsUpdate);
this.updateSliderButtonsView(); this.updateSliderButtonsView();
if (this.pendingChangeSlide) {
this.pendingChangeSlide = false;
this.inlineSlider.scrollToCurrentSlide();
}
} }
updateSliderButtonsView() { updateSliderButtonsView() {
if (!this.refs.itemsListWrap) return;
const container = this.refs.itemsListWrap;
const scrollLeft = container.scrollLeft;
const scrollWidth = container.scrollWidth;
const clientWidth = container.clientWidth;
// Show prev arrow if we can scroll left
const canScrollLeft = scrollLeft > 1;
// Show next arrow if we can scroll right
const canScrollRight = scrollLeft < scrollWidth - clientWidth - 1;
this.setState({ this.setState({
displayPrev: this.inlineSlider.hasPreviousSlide(), displayPrev: canScrollLeft,
displayNext: this.inlineSlider.hasNextSlide(), displayNext: canScrollRight,
}); });
} }
onToggleSearchField() { onToggleSearchField() {
this.updateSlider(); setTimeout(() => this.updateSlider(), 100);
} }
render() { render() {
@ -404,9 +425,11 @@ class NavMenuInlineTabs extends React.PureComponent {
/> />
) : null} ) : null}
<li className="media-search"> {!['about', 'playlists'].includes(this.props.type) ? (
<ProfileSearchBar onQueryChange={this.props.onQueryChange} toggleSearchField={this.onToggleSearchField} type={this.props.type} /> <li className="media-search">
</li> <ProfileSearchBar onQueryChange={this.props.onQueryChange} toggleSearchField={this.onToggleSearchField} type={this.props.type} />
</li>
) : 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')}>

View File

@ -868,15 +868,6 @@ export class ProfileMediaPage extends Page {
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';
console.log('Filter Debug:', {
filterArgs: this.state.filterArgs,
selectedTag: this.state.selectedTag,
selectedSort: this.state.selectedSort,
hasActiveFilters,
hasActiveTags,
hasActiveSort
});
return [ return [
this.state.author ? ( this.state.author ? (
<ProfilePagesHeader <ProfilePagesHeader

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long