Skip to content

Commit

Permalink
Merge remote-tracking branch 'parent/main' into kb_migration
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Sep 6, 2023
2 parents 3ec70d2 + 9d290c2 commit fdf1d6d
Show file tree
Hide file tree
Showing 41 changed files with 750 additions and 567 deletions.
54 changes: 43 additions & 11 deletions CHANGELOG.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4
# This needs to be bullseye-slim because the Ruby image is built on bullseye-slim
ARG NODE_VERSION="16.20-bullseye-slim"
# This needs to be bookworm-slim because the Ruby image is built on bookworm-slim
ARG NODE_VERSION="16.20-bookworm-slim"

FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby
FROM node:${NODE_VERSION} as build
Expand All @@ -20,7 +20,7 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential \
git \
libicu-dev \
libidn11-dev \
libidn-dev \
libpq-dev \
libjemalloc-dev \
zlib1g-dev \
Expand Down Expand Up @@ -64,13 +64,13 @@ RUN apt-get update && \
apt-get -y --no-install-recommends install whois \
wget \
procps \
libssl1.1 \
libssl3 \
libpq5 \
imagemagick \
ffmpeg \
libjemalloc2 \
libicu67 \
libidn11 \
libicu72 \
libidn12 \
libyaml-0-2 \
file \
ca-certificates \
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/api/v1/peers/search_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ def set_domains
domain = TagManager.instance.normalize_domain(domain)
@domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain)
end
rescue Addressable::URI::InvalidURIError
@domains = []
end
end
1 change: 1 addition & 0 deletions app/helpers/media_component_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def render_video_component(status, **options)
blurhash: video.blurhash,
frameRate: meta.dig('original', 'frame_rate'),
inline: true,
aspectRatio: "#{meta.dig('original', 'width')} / #{meta.dig('original', 'height')}",
media: [
serialize_media_attachment(video),
].as_json,
Expand Down
22 changes: 13 additions & 9 deletions app/javascript/mastodon/actions/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ export function submitSearch(type) {
const signedIn = !!getState().getIn(['meta', 'me']);

if (value.length === 0) {
dispatch(fetchSearchSuccess({ accounts: [], statuses: [], hashtags: [] }, ''));
dispatch(fetchSearchSuccess({ accounts: [], statuses: [], hashtags: [] }, '', type));
return;
}

dispatch(fetchSearchRequest());
dispatch(fetchSearchRequest(type));

api(getState).get('/api/v2/search', {
params: {
q: value,
resolve: signedIn,
limit: 10,
limit: 11,
type,
},
}).then(response => {
Expand All @@ -59,24 +59,26 @@ export function submitSearch(type) {
dispatch(importFetchedStatuses(response.data.statuses));
}

dispatch(fetchSearchSuccess(response.data, value));
dispatch(fetchSearchSuccess(response.data, value, type));
dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
}).catch(error => {
dispatch(fetchSearchFail(error));
});
};
}

export function fetchSearchRequest() {
export function fetchSearchRequest(searchType) {
return {
type: SEARCH_FETCH_REQUEST,
searchType,
};
}

export function fetchSearchSuccess(results, searchTerm) {
export function fetchSearchSuccess(results, searchTerm, searchType) {
return {
type: SEARCH_FETCH_SUCCESS,
results,
searchType,
searchTerm,
};
}
Expand All @@ -90,15 +92,16 @@ export function fetchSearchFail(error) {

export const expandSearch = type => (dispatch, getState) => {
const value = getState().getIn(['search', 'value']);
const offset = getState().getIn(['search', 'results', type]).size;
const offset = getState().getIn(['search', 'results', type]).size - 1;

dispatch(expandSearchRequest());
dispatch(expandSearchRequest(type));

api(getState).get('/api/v2/search', {
params: {
q: value,
type,
offset,
limit: 11,
},
}).then(({ data }) => {
if (data.accounts) {
Expand All @@ -116,8 +119,9 @@ export const expandSearch = type => (dispatch, getState) => {
});
};

export const expandSearchRequest = () => ({
export const expandSearchRequest = (searchType) => ({
type: SEARCH_EXPAND_REQUEST,
searchType,
});

export const expandSearchSuccess = (results, searchTerm, searchType) => ({
Expand Down
25 changes: 3 additions & 22 deletions app/javascript/mastodon/components/animated_number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,10 @@ import { reduceMotion } from '../initial_state';

import { ShortNumber } from './short_number';

const obfuscatedCount = (count: number) => {
if (count < 0) {
return 0;
} else if (count <= 1) {
return count;
} else {
return '1+';
}
};

interface Props {
value: number;
obfuscate?: boolean;
}
export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
export const AnimatedNumber: React.FC<Props> = ({ value }) => {
const [previousValue, setPreviousValue] = useState(value);
const [direction, setDirection] = useState<1 | -1>(1);

Expand All @@ -36,11 +25,7 @@ export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
);

if (reduceMotion) {
return obfuscate ? (
<>{obfuscatedCount(value)}</>
) : (
<ShortNumber value={value} />
);
return <ShortNumber value={value} />;
}

const styles = [
Expand All @@ -67,11 +52,7 @@ export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
transform: `translateY(${style.y * 100}%)`,
}}
>
{obfuscate ? (
obfuscatedCount(data as number)
) : (
<ShortNumber value={data as number} />
)}
<ShortNumber value={data as number} />
</span>
))}
</span>
Expand Down
4 changes: 1 addition & 3 deletions app/javascript/mastodon/components/icon_button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ interface Props {
overlay: boolean;
tabIndex: number;
counter?: number;
obfuscateCount?: boolean;
href?: string;
ariaHidden: boolean;
data_id?: string;
Expand Down Expand Up @@ -106,7 +105,6 @@ export class IconButton extends PureComponent<Props, States> {
tabIndex,
title,
counter,
obfuscateCount,
href,
ariaHidden,
data_id,
Expand All @@ -133,7 +131,7 @@ export class IconButton extends PureComponent<Props, States> {
<Icon id={icon} fixedWidth aria-hidden='true' />{' '}
{typeof counter !== 'undefined' && (
<span className='icon-button__counter'>
<AnimatedNumber value={counter} obfuscate={obfuscateCount} />
<AnimatedNumber value={counter} />
</span>
)}
</>
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/mastodon/components/status_action_bar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ class StatusActionBar extends ImmutablePureComponent {

return (
<div className='status__action-bar'>
<IconButton className='status__action-bar__button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
<IconButton className='status__action-bar__button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
Expand Down
120 changes: 31 additions & 89 deletions app/javascript/mastodon/features/compose/components/search_results.jsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,37 @@
import PropTypes from 'prop-types';

import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage } from 'react-intl';

import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';

import { Icon } from 'mastodon/components/icon';
import { LoadMore } from 'mastodon/components/load_more';
import { SearchSection } from 'mastodon/features/explore/components/search_section';

import { ImmutableHashtag as Hashtag } from '../../../components/hashtag';
import AccountContainer from '../../../containers/account_container';
import StatusContainer from '../../../containers/status_container';
import { searchEnabled } from '../../../initial_state';

const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
});
const INITIAL_PAGE_LIMIT = 10;

const withoutLastResult = list => {
if (list.size > INITIAL_PAGE_LIMIT && list.size % INITIAL_PAGE_LIMIT === 1) {
return list.skipLast(1);
} else {
return list;
}
};

class SearchResults extends ImmutablePureComponent {

static propTypes = {
results: ImmutablePropTypes.map.isRequired,
suggestions: ImmutablePropTypes.list.isRequired,
fetchSuggestions: PropTypes.func.isRequired,
expandSearch: PropTypes.func.isRequired,
dismissSuggestion: PropTypes.func.isRequired,
searchTerm: PropTypes.string,
intl: PropTypes.object.isRequired,
noMoreResults: ImmutablePropTypes.map,
};

componentDidMount () {
if (this.props.searchTerm === '') {
this.props.fetchSuggestions();
}
}

componentDidUpdate () {
if (this.props.searchTerm === '') {
this.props.fetchSuggestions();
}
}

handleLoadMoreAccounts = () => this.props.expandSearch('accounts');

handleLoadMoreStatuses = () => this.props.expandSearch('statuses');
Expand All @@ -51,100 +41,52 @@ class SearchResults extends ImmutablePureComponent {
showMoreResults = (searchType) => this.props.noMoreResults ? !this.props.noMoreResults.get(searchType) : true;

render () {
const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props;

if (searchTerm === '' && !suggestions.isEmpty()) {
return (
<div className='search-results'>
<div className='trends'>
<div className='trends__header'>
<Icon id='user-plus' fixedWidth />
<FormattedMessage id='suggestions.header' defaultMessage='You might be interested in…' />
</div>

{suggestions && suggestions.map(suggestion => (
<AccountContainer
key={suggestion.get('account')}
id={suggestion.get('account')}
actionIcon={suggestion.get('source') === 'past_interactions' ? 'times' : null}
actionTitle={suggestion.get('source') === 'past_interactions' ? intl.formatMessage(messages.dismissSuggestion) : null}
onActionClick={dismissSuggestion}
/>
))}
</div>
</div>
);
}
const { results } = this.props;

let accounts, statuses, hashtags;
let count = 0;

if (results.get('accounts') && results.get('accounts').size > 0) {
count += results.get('accounts').size;
const showMore = this.showMoreResults('accounts');
accounts = (
<div className='search-results__section'>
<h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></h5>

{results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
<SearchSection title={<><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}>
{withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)}
{(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
</SearchSection>
);
}

{showMore && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
</div>
if (results.get('hashtags') && results.get('hashtags').size > 0) {
hashtags = (
<SearchSection title={<><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}>
{withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
{(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
</SearchSection>
);
}

if (results.get('statuses') && results.get('statuses').size > 0) {
count += results.get('statuses').size;
const showMore = this.showMoreResults('statuses');
statuses = (
<div className='search-results__section'>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></h5>

{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}

{showMore && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
</div>
);
} else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) {
statuses = (
<div className='search-results__section'>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></h5>

<div className='search-results__info'>
<FormattedMessage id='search_results.statuses_fts_disabled' defaultMessage='Searching posts by their content is not enabled on this Mastodon server.' />
</div>
</div>
<SearchSection title={<><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}>
{withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)}
{(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
</SearchSection>
);
}

if (results.get('hashtags') && results.get('hashtags').size > 0) {
count += results.get('hashtags').size;
const showMore = this.showMoreResults('hashtags');
hashtags = (
<div className='search-results__section'>
<h5><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>

{results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}

{showMore && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
</div>
);
}

return (
<div className='search-results'>
<div className='search-results__header'>
<Icon id='search' fixedWidth />
<FormattedMessage id='search_results.total' defaultMessage='{count, plural, one {# result} other {# results}}' values={{ count }} />
<FormattedMessage id='explore.search_results' defaultMessage='Search results' />
</div>

{accounts}
{statuses}
{hashtags}
{statuses}
</div>
);
}

}

export default injectIntl(SearchResults);
export default SearchResults;
Loading

0 comments on commit fdf1d6d

Please sign in to comment.