diff --git a/.gitignore b/.gitignore index 7d76b827511f4..ce6d6ed771c3c 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,7 @@ yarn-debug.log # Ignore Docker option files docker-compose.override.yml + +# Add by highemerly +public/announcements.json +public/server-info.html diff --git a/Gemfile b/Gemfile index 355b7e43f87ad..9eb93b4c5e589 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ gem 'puma', '~> 5.6' gem 'rails', '~> 6.1.7' gem 'sprockets', '~> 3.7.2' gem 'thor', '~> 1.2' -gem 'rack', '~> 2.2.4' +gem 'rack', '~> 2.2.6' gem 'hamlit-rails', '~> 0.2' gem 'pg', '~> 1.4' diff --git a/Gemfile.lock b/Gemfile.lock index b6e09e5dfe105..d818a02e880ec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -483,7 +483,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.6.0) - rack (2.2.4) + rack (2.2.6.3) rack-attack (6.6.1) rack (>= 1.0, < 3) rack-cors (1.1.1) @@ -812,7 +812,7 @@ DEPENDENCIES pry-rails (~> 0.3) puma (~> 5.6) pundit (~> 2.2) - rack (~> 2.2.4) + rack (~> 2.2.6) rack-attack (~> 6.6) rack-cors (~> 1.1) rack-test (~> 2.0) diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 6dd4fb18b024d..6f5d0bcca79f0 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -4,30 +4,21 @@ class StatusesIndex < Chewy::Index include FormattingHelper settings index: { refresh_interval: '30s' }, analysis: { - filter: { - english_stop: { - type: 'stop', - stopwords: '_english_', - }, - english_stemmer: { - type: 'stemmer', - language: 'english', - }, - english_possessive_stemmer: { - type: 'stemmer', - language: 'possessive_english', + tokenizer: { + kuromoji_user_dict: { + type: 'kuromoji_tokenizer', + user_dictionary: 'userdic.txt', }, }, analyzer: { content: { - tokenizer: 'uax_url_email', + type: 'custom', + tokenizer: 'kuromoji_user_dict', filter: %w( - english_possessive_stemmer - lowercase - asciifolding + kuromoji_baseform + kuromoji_stemmer cjk_width - english_stop - english_stemmer + lowercase ), }, }, diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index f5d5c12449b52..956d2e586f378 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -52,6 +52,7 @@ def user_settings_params :setting_advanced_layout, :setting_use_blurhash, :setting_use_pending_items, + :setting_hidden_direct, :setting_trends, :setting_crop_images, :setting_always_send_emails, diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index a9b7efc4a3dd9..3882a51edc7fd 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -520,7 +520,11 @@ export function fetchComposeSuggestions(token) { return (dispatch, getState) => { switch (token[0]) { case ':': - fetchComposeSuggestionsEmojis(dispatch, getState, token); + if (token[1] == '@') { + fetchComposeSuggestionsAccounts(dispatch, getState, token.substr(1, token.length)); + } else { + fetchComposeSuggestionsEmojis(dispatch, getState, token); + } break; case '#': fetchComposeSuggestionsTags(dispatch, getState, token); @@ -569,6 +573,9 @@ export function selectComposeSuggestion(position, token, suggestion, path) { } else if (suggestion.type === 'account') { completion = getState().getIn(['accounts', suggestion.id, 'acct']); startPosition = position; + if (token[0] == ':') { + completion = `@${completion}:`; + } } // We don't want to replace hashtags that vary only in case due to accessibility, but we need to fire off an event so that diff --git a/app/javascript/mastodon/components/display_name.js b/app/javascript/mastodon/components/display_name.js index e9139ab0faf7d..06a7e2a952d06 100644 --- a/app/javascript/mastodon/components/display_name.js +++ b/app/javascript/mastodon/components/display_name.js @@ -62,8 +62,14 @@ export default class DisplayName extends React.PureComponent { acct = `${acct}@${localDomain}`; } + let suffixClassName = 'display-name__account'; + + if (acct.indexOf('@') === -1) { + suffixClassName = suffixClassName + ' handon-local-account'; + } + displayName = ; - suffix = @{acct}; + suffix = @{acct}; } else { displayName = ; suffix = ; diff --git a/app/javascript/mastodon/components/logo.js b/app/javascript/mastodon/components/logo.js index ee5c22496cc60..177e6d8d02e1d 100644 --- a/app/javascript/mastodon/components/logo.js +++ b/app/javascript/mastodon/components/logo.js @@ -1,10 +1,7 @@ import React from 'react'; const Logo = () => ( - - Mastodon - - + ); export default Logo; diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index a1384ba5838db..f10c29b0cc0a9 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -510,6 +510,7 @@ class Status extends ImmutablePureComponent { {prepend}
+
diff --git a/app/javascript/mastodon/features/compose/components/announcements.js b/app/javascript/mastodon/features/compose/components/announcements.js new file mode 100644 index 0000000000000..08d45ff181fd0 --- /dev/null +++ b/app/javascript/mastodon/features/compose/components/announcements.js @@ -0,0 +1,120 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Immutable from 'immutable'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; +import classnames from 'classnames'; + +class Announcement extends React.PureComponent { + + static propTypes = { + item: ImmutablePropTypes.map, + } + + render() { + const { item } = this.props; + + const contents = []; + contents.push(
{item.get('body')}
); + if (item.get('icon')) { + contents.push( +
+ +
+ ); + } + + const href = item.get('href'); + + const classname = classnames({ + 'announcements2__item': true, + 'announcements2__item--clickable': !!href, + }); + + if (!href) { + return (
{contents}
); + } else if (href.startsWith('/web/')) { + return ({contents}); + } else { + return (
{contents}); + } + } + +} + +export default class Announcements extends React.PureComponent { + + state = { + items: Announcements.cache || Immutable.Map(), + } + + static isCacheControlled = false + static lastDate = null + static cache = null + + constructor () { + super(); + this.refresh(); + } + + componentWillUnmount() { + this.cancelPolling(); + } + + setPolling = () => { + this.timer = setTimeout(this.refresh, 2 * 60 * 1000); + } + + cancelPolling = () => { + if (this.timer !== null) { + clearTimeout(this.timer); + this.timer = null; + } + } + + deleteServiceWorkerCache = () => { + // files in /system/ will be cached by SW + if (self.caches) { + return caches.open('mastodon-system') + .then(cache => cache.delete(window.origin + '/system/announcements.json')) + .catch(() => {}); + } else { + return Promise.resolve(); + } + } + + refresh = () => { + this.timer = null; + + axios.get('/announcements.json', { + headers: { + 'If-Modified-Since': !Announcements.isCacheControlled && Announcements.lastDate || '', + }, + }) + .then(resp => { + Announcements.isCacheControlled = !!resp.headers['cache-control']; + Announcements.lastDate = resp.headers['last-modified']; + return resp; + }) + .then(resp => this.setState({ items: Announcements.cache = Immutable.fromJS(resp.data) || {} })) + .catch(err => err.response.status !== 304 && console.warn(err)) + .then(this.deleteServiceWorkerCache) + .then(this.setPolling) + .catch(err => err && console.warn(err)); + } + + render() { + const { items } = this.state; + + return ( +
    + {items.entrySeq().map(([key, item]) => + (
  • + +
  • ) + )} +
+ ); + } + +} diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js index ede29b8a08a97..763343d7d65b1 100644 --- a/app/javascript/mastodon/features/compose/components/poll_form.js +++ b/app/javascript/mastodon/features/compose/components/poll_form.js @@ -157,13 +157,16 @@ class PollForm extends ImmutablePureComponent {
- + {/* eslint-disable-next-line jsx-a11y/no-onchange */}