Merge commit 'aea67d448b
' into kb_migration
This commit is contained in:
commit
bb2e964dca
74 changed files with 747 additions and 870 deletions
|
@ -81,6 +81,15 @@ module.exports = {
|
||||||
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
||||||
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
||||||
],
|
],
|
||||||
|
'no-restricted-syntax': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
selector: 'Literal[value=/•/], JSXText[value=/•/]',
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
message: "Use '·' (middle dot) instead of '•' (bullet)",
|
||||||
|
},
|
||||||
|
],
|
||||||
'no-self-assign': 'off',
|
'no-self-assign': 'off',
|
||||||
'no-unused-expressions': 'error',
|
'no-unused-expressions': 'error',
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
|
|
|
@ -4,6 +4,11 @@ exclude:
|
||||||
- 'vendor/**/*'
|
- 'vendor/**/*'
|
||||||
- lib/templates/haml/scaffold/_form.html.haml
|
- lib/templates/haml/scaffold/_form.html.haml
|
||||||
|
|
||||||
|
require:
|
||||||
|
- ./lib/linter/haml_middle_dot.rb
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
AltText:
|
AltText:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
MiddleDot:
|
||||||
|
enabled: true
|
||||||
|
|
|
@ -61,7 +61,7 @@ docker-compose.override.yml
|
||||||
/app/javascript/mastodon/features/emoji/emoji_map.json
|
/app/javascript/mastodon/features/emoji/emoji_map.json
|
||||||
|
|
||||||
# Ignore locale files
|
# Ignore locale files
|
||||||
/app/javascript/mastodon/locales
|
/app/javascript/mastodon/locales/*.json
|
||||||
/config/locales
|
/config/locales
|
||||||
|
|
||||||
# Ignore vendored CSS reset
|
# Ignore vendored CSS reset
|
||||||
|
|
|
@ -11,6 +11,7 @@ require:
|
||||||
- rubocop-rspec
|
- rubocop-rspec
|
||||||
- rubocop-performance
|
- rubocop-performance
|
||||||
- rubocop-capybara
|
- rubocop-capybara
|
||||||
|
- ./lib/linter/rubocop_middle_dot
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 3.0 # Set to minimum supported version of CI
|
TargetRubyVersion: 3.0 # Set to minimum supported version of CI
|
||||||
|
@ -213,3 +214,6 @@ Style/TrailingCommaInArrayLiteral:
|
||||||
# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral
|
# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral
|
||||||
Style/TrailingCommaInHashLiteral:
|
Style/TrailingCommaInHashLiteral:
|
||||||
EnforcedStyleForMultiline: 'comma'
|
EnforcedStyleForMultiline: 'comma'
|
||||||
|
|
||||||
|
Style/MiddleDot:
|
||||||
|
Enabled: true
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -5,7 +5,7 @@ ruby '>= 3.0.0'
|
||||||
|
|
||||||
gem 'pkg-config', '~> 1.5'
|
gem 'pkg-config', '~> 1.5'
|
||||||
|
|
||||||
gem 'puma', '~> 6.2'
|
gem 'puma', '~> 6.3'
|
||||||
gem 'rails', '~> 6.1.7'
|
gem 'rails', '~> 6.1.7'
|
||||||
gem 'sprockets', '~> 3.7.2'
|
gem 'sprockets', '~> 3.7.2'
|
||||||
gem 'thor', '~> 1.2'
|
gem 'thor', '~> 1.2'
|
||||||
|
|
|
@ -501,7 +501,7 @@ GEM
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
private_address_check (0.5.0)
|
private_address_check (0.5.0)
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.1)
|
||||||
puma (6.2.2)
|
puma (6.3.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.3.0)
|
pundit (2.3.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
|
@ -649,7 +649,7 @@ GEM
|
||||||
redis (>= 4.5.0, < 5)
|
redis (>= 4.5.0, < 5)
|
||||||
sidekiq-bulk (0.2.0)
|
sidekiq-bulk (0.2.0)
|
||||||
sidekiq
|
sidekiq
|
||||||
sidekiq-scheduler (5.0.2)
|
sidekiq-scheduler (5.0.3)
|
||||||
rufus-scheduler (~> 3.2)
|
rufus-scheduler (~> 3.2)
|
||||||
sidekiq (>= 6, < 8)
|
sidekiq (>= 6, < 8)
|
||||||
tilt (>= 1.4.0)
|
tilt (>= 1.4.0)
|
||||||
|
@ -847,7 +847,7 @@ DEPENDENCIES
|
||||||
premailer-rails
|
premailer-rails
|
||||||
private_address_check (~> 0.5)
|
private_address_check (~> 0.5)
|
||||||
public_suffix (~> 5.0)
|
public_suffix (~> 5.0)
|
||||||
puma (~> 6.2)
|
puma (~> 6.3)
|
||||||
pundit (~> 2.3)
|
pundit (~> 2.3)
|
||||||
rack (~> 2.2.7)
|
rack (~> 2.2.7)
|
||||||
rack-attack (~> 6.6)
|
rack-attack (~> 6.6)
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Settings::ImportsController < Settings::BaseController
|
||||||
muting: 'muted_accounts_failures.csv',
|
muting: 'muted_accounts_failures.csv',
|
||||||
domain_blocking: 'blocked_domains_failures.csv',
|
domain_blocking: 'blocked_domains_failures.csv',
|
||||||
bookmarks: 'bookmarks_failures.csv',
|
bookmarks: 'bookmarks_failures.csv',
|
||||||
|
lists: 'lists_failures.csv',
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
TYPE_TO_HEADERS_MAP = {
|
TYPE_TO_HEADERS_MAP = {
|
||||||
|
@ -20,6 +21,7 @@ class Settings::ImportsController < Settings::BaseController
|
||||||
muting: ['Account address', 'Hide notifications'],
|
muting: ['Account address', 'Hide notifications'],
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
bookmarks: false,
|
bookmarks: false,
|
||||||
|
lists: false,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -49,6 +51,8 @@ class Settings::ImportsController < Settings::BaseController
|
||||||
csv << [row.data['domain']]
|
csv << [row.data['domain']]
|
||||||
when :bookmarks
|
when :bookmarks
|
||||||
csv << [row.data['uri']]
|
csv << [row.data['uri']]
|
||||||
|
when :lists
|
||||||
|
csv << [row.data['list_name'], row.data['acct']]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ module ReactComponentHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def react_admin_component(name, props = {})
|
def react_admin_component(name, props = {})
|
||||||
data = { 'admin-component': name.to_s.camelcase, props: Oj.dump({ locale: I18n.locale }.merge(props)) }
|
data = { 'admin-component': name.to_s.camelcase, props: Oj.dump(props) }
|
||||||
div_tag_with_data(data)
|
div_tag_with_data(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ import {
|
||||||
fillListTimelineGaps,
|
fillListTimelineGaps,
|
||||||
} from './timelines';
|
} from './timelines';
|
||||||
|
|
||||||
const { messages } = getLocale();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} max
|
* @param {number} max
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
|
@ -43,8 +41,10 @@ const randomUpTo = max =>
|
||||||
* @param {function(object): boolean} [options.accept]
|
* @param {function(object): boolean} [options.accept]
|
||||||
* @returns {function(): void}
|
* @returns {function(): void}
|
||||||
*/
|
*/
|
||||||
export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) =>
|
export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => {
|
||||||
connectStream(channelName, params, (dispatch, getState) => {
|
const { messages } = getLocale();
|
||||||
|
|
||||||
|
return connectStream(channelName, params, (dispatch, getState) => {
|
||||||
const locale = getState().getIn(['meta', 'locale']);
|
const locale = getState().getIn(['meta', 'locale']);
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
@ -125,6 +125,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Function} dispatch
|
* @param {Function} dispatch
|
||||||
|
|
|
@ -144,7 +144,7 @@ class Account extends ImmutablePureComponent {
|
||||||
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
|
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
|
||||||
|
|
||||||
if (firstVerifiedField) {
|
if (firstVerifiedField) {
|
||||||
verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} /></>;
|
verification = <VerifiedBadge link={firstVerifiedField.get('value')} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -155,9 +155,13 @@ class Account extends ImmutablePureComponent {
|
||||||
<Avatar account={account} size={size} />
|
<Avatar account={account} size={size} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className='account__contents'>
|
||||||
<DisplayName account={account} />
|
<DisplayName account={account} />
|
||||||
{!minimal && <><ShortNumber value={account.get('followers_count')} isHide={account.getIn(['other_settings', 'hide_followers_count']) || false} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}</>}
|
{!minimal && (
|
||||||
|
<div className='account__details'>
|
||||||
|
<ShortNumber value={account.get('followers_count')} isHide={account.getIn(['other_settings', 'hide_followers_count']) || false} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,9 @@ class Poll extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
static getDerivedStateFromProps (props, state) {
|
static getDerivedStateFromProps (props, state) {
|
||||||
const { poll, intl } = props;
|
const { poll } = props;
|
||||||
const expires_at = poll.get('expires_at');
|
const expires_at = poll.get('expires_at');
|
||||||
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
|
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < Date.now();
|
||||||
return (expired === state.expired) ? null : { expired };
|
return (expired === state.expired) ? null : { expired };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +76,10 @@ class Poll extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupTimer () {
|
_setupTimer () {
|
||||||
const { poll, intl } = this.props;
|
const { poll } = this.props;
|
||||||
clearTimeout(this._timer);
|
clearTimeout(this._timer);
|
||||||
if (!this.state.expired) {
|
if (!this.state.expired) {
|
||||||
const delay = (new Date(poll.get('expires_at'))).getTime() - intl.now();
|
const delay = (new Date(poll.get('expires_at'))).getTime() - Date.now();
|
||||||
this._timer = setTimeout(() => {
|
this._timer = setTimeout(() => {
|
||||||
this.setState({ expired: true });
|
this.setState({ expired: true });
|
||||||
}, delay);
|
}, delay);
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'mastodon/locales';
|
||||||
|
|
||||||
import { getLocale, onProviderError } from '../locales';
|
|
||||||
|
|
||||||
const { messages } = getLocale();
|
|
||||||
|
|
||||||
export default class AdminComponent extends PureComponent {
|
export default class AdminComponent extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
locale: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { locale, children } = this.props;
|
const { children } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={messages} onError={onProviderError}>
|
<IntlProvider>
|
||||||
{children}
|
{children}
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
|
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
import { fetchCustomEmojis } from '../actions/custom_emojis';
|
import { fetchCustomEmojis } from '../actions/custom_emojis';
|
||||||
import { hydrateStore } from '../actions/store';
|
import { hydrateStore } from '../actions/store';
|
||||||
import Compose from '../features/standalone/compose';
|
import Compose from '../features/standalone/compose';
|
||||||
import initialState from '../initial_state';
|
import initialState from '../initial_state';
|
||||||
import { getLocale, onProviderError } from '../locales';
|
import { IntlProvider } from '../locales';
|
||||||
import { store } from '../store';
|
import { store } from '../store';
|
||||||
|
|
||||||
const { messages } = getLocale();
|
|
||||||
|
|
||||||
if (initialState) {
|
if (initialState) {
|
||||||
store.dispatch(hydrateStore(initialState));
|
store.dispatch(hydrateStore(initialState));
|
||||||
|
@ -20,17 +16,11 @@ if (initialState) {
|
||||||
|
|
||||||
store.dispatch(fetchCustomEmojis());
|
store.dispatch(fetchCustomEmojis());
|
||||||
|
|
||||||
export default class TimelineContainer extends PureComponent {
|
export default class ComposeContainer extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
locale: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { locale } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={messages} onError={onProviderError}>
|
<IntlProvider>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Compose />
|
<Compose />
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { BrowserRouter, Route } from 'react-router-dom';
|
import { BrowserRouter, Route } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -17,11 +15,9 @@ import { connectUserStream } from 'mastodon/actions/streaming';
|
||||||
import ErrorBoundary from 'mastodon/components/error_boundary';
|
import ErrorBoundary from 'mastodon/components/error_boundary';
|
||||||
import UI from 'mastodon/features/ui';
|
import UI from 'mastodon/features/ui';
|
||||||
import initialState, { title as siteTitle } from 'mastodon/initial_state';
|
import initialState, { title as siteTitle } from 'mastodon/initial_state';
|
||||||
import { getLocale, onProviderError } from 'mastodon/locales';
|
import { IntlProvider } from 'mastodon/locales';
|
||||||
import { store } from 'mastodon/store';
|
import { store } from 'mastodon/store';
|
||||||
|
|
||||||
const { messages } = getLocale();
|
|
||||||
|
|
||||||
const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`;
|
const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`;
|
||||||
|
|
||||||
const hydrateAction = hydrateStore(initialState);
|
const hydrateAction = hydrateStore(initialState);
|
||||||
|
@ -42,10 +38,6 @@ const createIdentityContext = state => ({
|
||||||
|
|
||||||
export default class Mastodon extends PureComponent {
|
export default class Mastodon extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
locale: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
static childContextTypes = {
|
static childContextTypes = {
|
||||||
identity: PropTypes.shape({
|
identity: PropTypes.shape({
|
||||||
signedIn: PropTypes.bool.isRequired,
|
signedIn: PropTypes.bool.isRequired,
|
||||||
|
@ -81,10 +73,8 @@ export default class Mastodon extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { locale } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={messages} onError={onProviderError}>
|
<IntlProvider>
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
|
|
@ -2,8 +2,6 @@ import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
|
|
||||||
import { fromJS } from 'immutable';
|
import { fromJS } from 'immutable';
|
||||||
|
|
||||||
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
||||||
|
@ -14,17 +12,14 @@ import Audio from 'mastodon/features/audio';
|
||||||
import Card from 'mastodon/features/status/components/card';
|
import Card from 'mastodon/features/status/components/card';
|
||||||
import MediaModal from 'mastodon/features/ui/components/media_modal';
|
import MediaModal from 'mastodon/features/ui/components/media_modal';
|
||||||
import Video from 'mastodon/features/video';
|
import Video from 'mastodon/features/video';
|
||||||
import { getLocale, onProviderError } from 'mastodon/locales';
|
import { IntlProvider } from 'mastodon/locales';
|
||||||
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
|
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
|
||||||
|
|
||||||
const { messages } = getLocale();
|
|
||||||
|
|
||||||
const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Hashtag, Audio };
|
const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Hashtag, Audio };
|
||||||
|
|
||||||
export default class MediaContainer extends PureComponent {
|
export default class MediaContainer extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
locale: PropTypes.string.isRequired,
|
|
||||||
components: PropTypes.object.isRequired,
|
components: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -73,7 +68,7 @@ export default class MediaContainer extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { locale, components } = this.props;
|
const { components } = this.props;
|
||||||
|
|
||||||
let handleOpenVideo;
|
let handleOpenVideo;
|
||||||
|
|
||||||
|
@ -83,7 +78,7 @@ export default class MediaContainer extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={messages} onError={onProviderError}>
|
<IntlProvider>
|
||||||
<>
|
<>
|
||||||
{[].map.call(components, (component, i) => {
|
{[].map.call(components, (component, i) => {
|
||||||
const componentName = component.getAttribute('data-component');
|
const componentName = component.getAttribute('data-component');
|
||||||
|
|
|
@ -247,7 +247,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
} else if (this.context.router) {
|
} else if (this.context.router) {
|
||||||
reblogLink = (
|
reblogLink = (
|
||||||
<>
|
<>
|
||||||
·
|
{' · '}
|
||||||
<Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'>
|
<Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'>
|
||||||
<Icon id={reblogIcon} />
|
<Icon id={reblogIcon} />
|
||||||
<span className='detailed-status__reblogs'>
|
<span className='detailed-status__reblogs'>
|
||||||
|
@ -259,7 +259,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
} else {
|
} else {
|
||||||
reblogLink = (
|
reblogLink = (
|
||||||
<>
|
<>
|
||||||
·
|
{' · '}
|
||||||
<a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
|
<a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
|
||||||
<Icon id={reblogIcon} />
|
<Icon id={reblogIcon} />
|
||||||
<span className='detailed-status__reblogs'>
|
<span className='detailed-status__reblogs'>
|
||||||
|
@ -313,7 +313,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
if (status.get('edited_at')) {
|
if (status.get('edited_at')) {
|
||||||
edited = (
|
edited = (
|
||||||
<>
|
<>
|
||||||
·
|
{' · '}
|
||||||
<EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} />
|
<EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { setLocale } from "./locales";
|
|
||||||
|
|
||||||
export async function loadLocale() {
|
|
||||||
const locale = document.querySelector('html').lang || 'en';
|
|
||||||
|
|
||||||
const localeData = await import(
|
|
||||||
/* webpackMode: "lazy" */
|
|
||||||
/* webpackChunkName: "locale/[request]" */
|
|
||||||
/* webpackInclude: /\.json$/ */
|
|
||||||
/* webpackPreload: true */
|
|
||||||
`mastodon/locales/${locale}.json`);
|
|
||||||
|
|
||||||
setLocale({ messages: localeData });
|
|
||||||
}
|
|
22
app/javascript/mastodon/locales/global_locale.ts
Normal file
22
app/javascript/mastodon/locales/global_locale.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
export interface LocaleData {
|
||||||
|
locale: string;
|
||||||
|
messages: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let loadedLocale: LocaleData;
|
||||||
|
|
||||||
|
export function setLocale(locale: LocaleData) {
|
||||||
|
loadedLocale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLocale() {
|
||||||
|
if (!loadedLocale && process.env.NODE_ENV === 'development') {
|
||||||
|
throw new Error('getLocale() called before any locale has been set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadedLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLocaleLoaded() {
|
||||||
|
return !!loadedLocale;
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
let theLocale;
|
|
||||||
|
|
||||||
export function setLocale(locale) {
|
|
||||||
theLocale = locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLocale() {
|
|
||||||
return theLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function onProviderError(error) {
|
|
||||||
// Silent the error, like upstream does
|
|
||||||
if(process.env.NODE_ENV === 'production') return;
|
|
||||||
|
|
||||||
// This browser does not advertise Intl support for this locale, we only print a warning
|
|
||||||
// As-per the spec, the browser should select the best matching locale
|
|
||||||
if(typeof error === "object" && error.message.match("MISSING_DATA")) {
|
|
||||||
console.warn(error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
5
app/javascript/mastodon/locales/index.ts
Normal file
5
app/javascript/mastodon/locales/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export type { LocaleData } from './global_locale';
|
||||||
|
export { setLocale, getLocale, isLocaleLoaded } from './global_locale';
|
||||||
|
export { loadLocale } from './load_locale';
|
||||||
|
|
||||||
|
export { IntlProvider } from './intl_provider';
|
56
app/javascript/mastodon/locales/intl_provider.tsx
Normal file
56
app/javascript/mastodon/locales/intl_provider.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { IntlProvider as BaseIntlProvider } from 'react-intl';
|
||||||
|
|
||||||
|
import { getLocale, isLocaleLoaded } from './global_locale';
|
||||||
|
import { loadLocale } from './load_locale';
|
||||||
|
|
||||||
|
function onProviderError(error: unknown) {
|
||||||
|
// Silent the error, like upstream does
|
||||||
|
if (process.env.NODE_ENV === 'production') return;
|
||||||
|
|
||||||
|
// This browser does not advertise Intl support for this locale, we only print a warning
|
||||||
|
// As-per the spec, the browser should select the best matching locale
|
||||||
|
if (
|
||||||
|
error &&
|
||||||
|
typeof error === 'object' &&
|
||||||
|
error instanceof Error &&
|
||||||
|
error.message.match('MISSING_DATA')
|
||||||
|
) {
|
||||||
|
console.warn(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IntlProvider: React.FC<
|
||||||
|
Omit<React.ComponentProps<typeof BaseIntlProvider>, 'locale' | 'messages'>
|
||||||
|
> = ({ children, ...props }) => {
|
||||||
|
const [localeLoaded, setLocaleLoaded] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function loadLocaleData() {
|
||||||
|
if (!isLocaleLoaded()) {
|
||||||
|
await loadLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocaleLoaded(true);
|
||||||
|
}
|
||||||
|
void loadLocaleData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!localeLoaded) return null;
|
||||||
|
|
||||||
|
const { locale, messages } = getLocale();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseIntlProvider
|
||||||
|
locale={locale}
|
||||||
|
messages={messages}
|
||||||
|
onError={onProviderError}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</BaseIntlProvider>
|
||||||
|
);
|
||||||
|
};
|
29
app/javascript/mastodon/locales/load_locale.ts
Normal file
29
app/javascript/mastodon/locales/load_locale.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Semaphore } from 'async-mutex';
|
||||||
|
|
||||||
|
import type { LocaleData } from './global_locale';
|
||||||
|
import { isLocaleLoaded, setLocale } from './global_locale';
|
||||||
|
|
||||||
|
const localeLoadingSemaphore = new Semaphore(1);
|
||||||
|
|
||||||
|
export async function loadLocale() {
|
||||||
|
const locale = document.querySelector<HTMLElement>('html')?.lang || 'en';
|
||||||
|
|
||||||
|
// We use a Semaphore here so only one thing can try to load the locales at
|
||||||
|
// the same time. If one tries to do it while its in progress, it will wait
|
||||||
|
// for the initial load to finish before it is resumed (and will see that locale
|
||||||
|
// data is already loaded)
|
||||||
|
await localeLoadingSemaphore.runExclusive(async () => {
|
||||||
|
// if the locale is already set, then do nothing
|
||||||
|
if (isLocaleLoaded()) return;
|
||||||
|
|
||||||
|
const localeData = (await import(
|
||||||
|
/* webpackMode: "lazy" */
|
||||||
|
/* webpackChunkName: "locale/[request]" */
|
||||||
|
/* webpackInclude: /\.json$/ */
|
||||||
|
/* webpackPreload: true */
|
||||||
|
`mastodon/locales/${locale}.json`
|
||||||
|
)) as LocaleData['messages'];
|
||||||
|
|
||||||
|
setLocale({ messages: localeData, locale });
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,221 +0,0 @@
|
||||||
# Custom Locale Data
|
|
||||||
|
|
||||||
This folder is used to store custom locale data. These custom locale data are
|
|
||||||
not yet provided by [Unicode Common Locale Data Repository](http://cldr.unicode.org/development/new-cldr-developers)
|
|
||||||
and hence not provided in [react-intl/locale-data/*](https://github.com/yahoo/react-intl).
|
|
||||||
|
|
||||||
The locale data should support [Locale Data APIs](https://github.com/yahoo/react-intl/wiki/API#locale-data-apis)
|
|
||||||
of the react-intl library.
|
|
||||||
|
|
||||||
It is recommended to start your custom locale data from this sample English
|
|
||||||
locale data ([*](#plural-rules)):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
/*eslint eqeqeq: "off"*/
|
|
||||||
/*eslint no-nested-ternary: "off"*/
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
locale: "en",
|
|
||||||
pluralRuleFunction: function(e, a) {
|
|
||||||
var n = String(e).split("."),
|
|
||||||
l = !n[1],
|
|
||||||
o = Number(n[0]) == e,
|
|
||||||
t = o && n[0].slice(-1),
|
|
||||||
r = o && n[0].slice(-2);
|
|
||||||
return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other"
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
year: {
|
|
||||||
displayName: "year",
|
|
||||||
relative: {
|
|
||||||
0: "this year",
|
|
||||||
1: "next year",
|
|
||||||
"-1": "last year"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} year",
|
|
||||||
other: "in {0} years"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} year ago",
|
|
||||||
other: "{0} years ago"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
displayName: "month",
|
|
||||||
relative: {
|
|
||||||
0: "this month",
|
|
||||||
1: "next month",
|
|
||||||
"-1": "last month"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} month",
|
|
||||||
other: "in {0} months"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} month ago",
|
|
||||||
other: "{0} months ago"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
day: {
|
|
||||||
displayName: "day",
|
|
||||||
relative: {
|
|
||||||
0: "today",
|
|
||||||
1: "tomorrow",
|
|
||||||
"-1": "yesterday"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} day",
|
|
||||||
other: "in {0} days"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} day ago",
|
|
||||||
other: "{0} days ago"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hour: {
|
|
||||||
displayName: "hour",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} hour",
|
|
||||||
other: "in {0} hours"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} hour ago",
|
|
||||||
other: "{0} hours ago"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
minute: {
|
|
||||||
displayName: "minute",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} minute",
|
|
||||||
other: "in {0} minutes"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} minute ago",
|
|
||||||
other: "{0} minutes ago"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
second: {
|
|
||||||
displayName: "second",
|
|
||||||
relative: {
|
|
||||||
0: "now"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} second",
|
|
||||||
other: "in {0} seconds"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} second ago",
|
|
||||||
other: "{0} seconds ago"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
### Plural Rules
|
|
||||||
|
|
||||||
The function `pluralRuleFunction()` should return the key to proper string of
|
|
||||||
a plural form(s). The purpose of the function is to provide key of translate
|
|
||||||
strings of correct plural form according. The different forms are described in
|
|
||||||
[CLDR's Plural Rules][cldr-plural-rules],
|
|
||||||
|
|
||||||
[cldr-plural-rules]: http://cldr.unicode.org/index/cldr-spec/plural-rules
|
|
||||||
|
|
||||||
#### Quick Overview on CLDR Rules
|
|
||||||
|
|
||||||
Let's take English as an example.
|
|
||||||
|
|
||||||
When you describe a number, you can be either describe it as:
|
|
||||||
* Cardinals: 1st, 2nd, 3rd ... 11th, 12th ... 21st, 22nd, 23nd ....
|
|
||||||
* Ordinals: 1, 2, 3 ...
|
|
||||||
|
|
||||||
In any of these cases, the nouns will reflect the number with singular or plural
|
|
||||||
form. For example:
|
|
||||||
* in 0 days
|
|
||||||
* in 1 day
|
|
||||||
* in 2 days
|
|
||||||
|
|
||||||
The `pluralRuleFunction` receives 2 parameters:
|
|
||||||
* `e`: a string representation of the number. Such as, "`1`", "`2`", "`2.1`".
|
|
||||||
* `a`: `true` if this is "cardinal" type of description. `false` for ordinal and other case.
|
|
||||||
|
|
||||||
#### How you should write `pluralRuleFunction`
|
|
||||||
|
|
||||||
The first rule to write pluralRuleFunction is never translate the output string
|
|
||||||
into your language. [Plural Rules][cldr-plural-rules] specified you should use
|
|
||||||
these as the return values:
|
|
||||||
|
|
||||||
* "`zero`"
|
|
||||||
* "`one`" (singular)
|
|
||||||
* "`two`" (dual)
|
|
||||||
* "`few`" (paucal)
|
|
||||||
* "`many`" (also used for fractions if they have a separate class)
|
|
||||||
* "`other`" (required—general plural form—also used if the language only has a single form)
|
|
||||||
|
|
||||||
Again, we'll use English as the example here.
|
|
||||||
|
|
||||||
Let's read the `return` statement in the pluralRuleFunction above:
|
|
||||||
```javascript
|
|
||||||
return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other"
|
|
||||||
```
|
|
||||||
|
|
||||||
This nested ternary is hard to read. It basically means:
|
|
||||||
```javascript
|
|
||||||
// e: the number variable to examine
|
|
||||||
// a: "true" if cardinals
|
|
||||||
// l: "true" if the variable e has nothin after decimal mark (e.g. "1.0" would be false)
|
|
||||||
// o: "true" if the variable e is an integer
|
|
||||||
// t: the "ones" of the number. e.g. "3" for number "9123"
|
|
||||||
// r: the "ones" and "tens" of the number. e.g. "23" for number "9123"
|
|
||||||
if (a == true) {
|
|
||||||
if (t == 1 && r != 11) {
|
|
||||||
return "one"; // i.e. 1st, 21st, 101st, 121st ...
|
|
||||||
} else if (t == 2 && r != 12) {
|
|
||||||
return "two"; // i.e. 2nd, 22nd, 102nd, 122nd ...
|
|
||||||
} else if (t == 3 && r != 13) {
|
|
||||||
return "few"; // i.e. 3rd, 23rd, 103rd, 123rd ...
|
|
||||||
} else {
|
|
||||||
return "other"; // i.e. 4th, 11th, 12th, 24th ...
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (e == 1 && l) {
|
|
||||||
return "one"; // i.e. 1 day
|
|
||||||
} else {
|
|
||||||
return "other"; // i.e. 0 days, 2 days, 3 days
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If your language, like French, do not have complicated cardinal rules, you may
|
|
||||||
use the French's version of it:
|
|
||||||
```javascript
|
|
||||||
function (e, a) {
|
|
||||||
return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If your language, like Chinese, do not have any pluralization rule at all you
|
|
||||||
may use the Chinese's version of it:
|
|
||||||
```javascript
|
|
||||||
function (e, a) {
|
|
||||||
return "other";
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*eslint eqeqeq: "off"*/
|
|
||||||
/*eslint no-nested-ternary: "off"*/
|
|
||||||
/*eslint quotes: "off"*/
|
|
||||||
|
|
||||||
const rules = [{
|
|
||||||
locale: "co",
|
|
||||||
pluralRuleFunction: function (e, a) {
|
|
||||||
return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other";
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
year: {
|
|
||||||
displayName: "annu",
|
|
||||||
relative: {
|
|
||||||
0: "quist'annu",
|
|
||||||
1: "l'annu chì vene",
|
|
||||||
"-1": "l'annu passatu",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} annu",
|
|
||||||
other: "in {0} anni",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} annu fà",
|
|
||||||
other: "{0} anni fà",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
displayName: "mese",
|
|
||||||
relative: {
|
|
||||||
0: "Questu mese",
|
|
||||||
1: "u mese chì vene",
|
|
||||||
"-1": "u mese passatu",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} mese",
|
|
||||||
other: "in {0} mesi",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} mese fà",
|
|
||||||
other: "{0} mesi fà",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
day: {
|
|
||||||
displayName: "ghjornu",
|
|
||||||
relative: {
|
|
||||||
0: "oghje",
|
|
||||||
1: "dumane",
|
|
||||||
"-1": "eri",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} ghjornu",
|
|
||||||
other: "in {0} ghjornu",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} ghjornu fà",
|
|
||||||
other: "{0} ghjorni fà",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hour: {
|
|
||||||
displayName: "ora",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} ora",
|
|
||||||
other: "in {0} ore",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} ora fà",
|
|
||||||
other: "{0} ore fà",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
minute: {
|
|
||||||
displayName: "minuta",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} minuta",
|
|
||||||
other: "in {0} minute",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} minuta fà",
|
|
||||||
other: "{0} minute fà",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
second: {
|
|
||||||
displayName: "siconda",
|
|
||||||
relative: {
|
|
||||||
0: "avà",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "in {0} siconda",
|
|
||||||
other: "in {0} siconde",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "{0} siconda fà",
|
|
||||||
other: "{0} siconde fà",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}];
|
|
||||||
|
|
||||||
export default rules;
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*eslint eqeqeq: "off"*/
|
|
||||||
/*eslint no-nested-ternary: "off"*/
|
|
||||||
/*eslint quotes: "off"*/
|
|
||||||
|
|
||||||
const rules = [{
|
|
||||||
locale: "oc",
|
|
||||||
pluralRuleFunction: function (e, a) {
|
|
||||||
return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other";
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
year: {
|
|
||||||
displayName: "an",
|
|
||||||
relative: {
|
|
||||||
0: "ongan",
|
|
||||||
1: "l'an que ven",
|
|
||||||
"-1": "l'an passat",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "d’aquí {0} an",
|
|
||||||
other: "d’aquí {0} ans",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "fa {0} an",
|
|
||||||
other: "fa {0} ans",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
displayName: "mes",
|
|
||||||
relative: {
|
|
||||||
0: "aqueste mes",
|
|
||||||
1: "lo mes que ven",
|
|
||||||
"-1": "lo mes passat",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "d’aquí {0} mes",
|
|
||||||
other: "d’aquí {0} meses",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "fa {0} mes",
|
|
||||||
other: "fa {0} meses",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
day: {
|
|
||||||
displayName: "jorn",
|
|
||||||
relative: {
|
|
||||||
0: "uèi",
|
|
||||||
1: "deman",
|
|
||||||
"-1": "ièr",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "d’aquí {0} jorn",
|
|
||||||
other: "d’aquí {0} jorns",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "fa {0} jorn",
|
|
||||||
other: "fa {0} jorns",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hour: {
|
|
||||||
displayName: "ora",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "d’aquí {0} ora",
|
|
||||||
other: "d’aquí {0} oras",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "fa {0} ora",
|
|
||||||
other: "fa {0} oras",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
minute: {
|
|
||||||
displayName: "minuta",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "d’aquí {0} minuta",
|
|
||||||
other: "d’aquí {0} minutas",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "fa {0} minuta",
|
|
||||||
other: "fa {0} minutas",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
second: {
|
|
||||||
displayName: "segonda",
|
|
||||||
relative: {
|
|
||||||
0: "ara",
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
one: "d’aquí {0} segonda",
|
|
||||||
other: "d’aquí {0} segondas",
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
one: "fa {0} segonda",
|
|
||||||
other: "fa {0} segondas",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}];
|
|
||||||
|
|
||||||
export default rules;
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*eslint eqeqeq: "off"*/
|
|
||||||
/*eslint no-nested-ternary: "off"*/
|
|
||||||
/*eslint quotes: "off"*/
|
|
||||||
/*eslint comma-dangle: "off"*/
|
|
||||||
|
|
||||||
const rules = [
|
|
||||||
{
|
|
||||||
locale: "sa",
|
|
||||||
fields: {
|
|
||||||
year: {
|
|
||||||
displayName: "year",
|
|
||||||
relative: {
|
|
||||||
0: "this year",
|
|
||||||
1: "next year",
|
|
||||||
"-1": "last year"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
other: "+{0} y"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
other: "-{0} y"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
displayName: "month",
|
|
||||||
relative: {
|
|
||||||
0: "this month",
|
|
||||||
1: "next month",
|
|
||||||
"-1": "last month"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
other: "+{0} m"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
other: "-{0} m"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
day: {
|
|
||||||
displayName: "day",
|
|
||||||
relative: {
|
|
||||||
0: "अद्य",
|
|
||||||
1: "श्वः",
|
|
||||||
"-1": "गतदिनम्"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
other: "+{0} d"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
other: "-{0} d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hour: {
|
|
||||||
displayName: "hour",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
other: "+{0} h"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
other: "-{0} h"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
minute: {
|
|
||||||
displayName: "minute",
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
other: "+{0} min"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
other: "-{0} min"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
second: {
|
|
||||||
displayName: "second",
|
|
||||||
relative: {
|
|
||||||
0: "now"
|
|
||||||
},
|
|
||||||
relativeTime: {
|
|
||||||
future: {
|
|
||||||
other: "+{0} s"
|
|
||||||
},
|
|
||||||
past: {
|
|
||||||
other: "-{0} s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export default rules;
|
|
|
@ -239,14 +239,14 @@ ready(() => {
|
||||||
|
|
||||||
[].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
|
[].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
|
||||||
const componentName = element.getAttribute('data-admin-component');
|
const componentName = element.getAttribute('data-admin-component');
|
||||||
const { locale, ...componentProps } = JSON.parse(element.getAttribute('data-props'));
|
const componentProps = JSON.parse(element.getAttribute('data-props'));
|
||||||
|
|
||||||
import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => {
|
import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => {
|
||||||
return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => {
|
return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => {
|
||||||
const root = createRoot(element);
|
const root = createRoot(element);
|
||||||
|
|
||||||
root.render (
|
root.render (
|
||||||
<AdminComponent locale={locale}>
|
<AdminComponent>
|
||||||
<Component {...componentProps} />
|
<Component {...componentProps} />
|
||||||
</AdminComponent>,
|
</AdminComponent>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import './public-path';
|
import './public-path';
|
||||||
|
import main from "mastodon/main"
|
||||||
|
|
||||||
import { start } from '../mastodon/common';
|
import { start } from '../mastodon/common';
|
||||||
import { loadLocale } from '../mastodon/load_locale';
|
import { loadLocale } from '../mastodon/locales';
|
||||||
import { loadPolyfills } from '../mastodon/polyfills';
|
import { loadPolyfills } from '../mastodon/polyfills';
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|
||||||
loadPolyfills().then(loadLocale).then(async () => {
|
loadPolyfills()
|
||||||
const { default: main } = await import('mastodon/main');
|
.then(loadLocale)
|
||||||
|
.then(main)
|
||||||
return main();
|
.catch(e => {
|
||||||
}).catch(e => {
|
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,8 +15,7 @@ import { start } from '../mastodon/common';
|
||||||
import { timeAgoString } from '../mastodon/components/relative_timestamp';
|
import { timeAgoString } from '../mastodon/components/relative_timestamp';
|
||||||
import emojify from '../mastodon/features/emoji/emoji';
|
import emojify from '../mastodon/features/emoji/emoji';
|
||||||
import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions';
|
import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions';
|
||||||
import { loadLocale } from '../mastodon/load_locale';
|
import { loadLocale, getLocale } from '../mastodon/locales';
|
||||||
import { getLocale } from '../mastodon/locales';
|
|
||||||
import { loadPolyfills } from '../mastodon/polyfills';
|
import { loadPolyfills } from '../mastodon/polyfills';
|
||||||
import ready from '../mastodon/ready';
|
import ready from '../mastodon/ready';
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { start } from '../mastodon/common';
|
import { start } from '../mastodon/common';
|
||||||
import ComposeContainer from '../mastodon/containers/compose_container';
|
import ComposeContainer from '../mastodon/containers/compose_container';
|
||||||
import { loadLocale } from '../mastodon/load_locale';
|
|
||||||
import { loadPolyfills } from '../mastodon/polyfills';
|
import { loadPolyfills } from '../mastodon/polyfills';
|
||||||
import ready from '../mastodon/ready';
|
import ready from '../mastodon/ready';
|
||||||
|
|
||||||
|
@ -26,6 +25,6 @@ function main() {
|
||||||
ready(loaded);
|
ready(loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPolyfills().then(loadLocale).then(main).catch(error => {
|
loadPolyfills().then(main).catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,11 +3,8 @@
|
||||||
display: block;
|
display: block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
overflow: hidden;
|
||||||
|
border-radius: 4px;
|
||||||
@media screen and (max-width: $no-gap-breakpoint) {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
|
@ -22,7 +19,6 @@
|
||||||
height: 130px;
|
height: 130px;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: darken($ui-base-color, 12%);
|
background: darken($ui-base-color, 12%);
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -30,7 +26,6 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (width <= 600px) {
|
@media screen and (width <= 600px) {
|
||||||
|
@ -45,11 +40,6 @@
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: lighten($ui-base-color, 4%);
|
background: lighten($ui-base-color, 4%);
|
||||||
border-radius: 0 0 4px 4px;
|
|
||||||
|
|
||||||
@media screen and (max-width: $no-gap-breakpoint) {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
|
@ -7949,13 +7949,28 @@ noscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account__contents {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account__details {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.verified-badge {
|
.verified-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: $valid-value-color;
|
color: $valid-value-color;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
> span {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
|
@ -137,6 +137,10 @@ code {
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
&.invited-by {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $highlight-text-color;
|
color: $highlight-text-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ class BulkImport < ApplicationRecord
|
||||||
muting: 2,
|
muting: 2,
|
||||||
domain_blocking: 3,
|
domain_blocking: 3,
|
||||||
bookmarks: 4,
|
bookmarks: 4,
|
||||||
|
lists: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum state: {
|
enum state: {
|
||||||
|
|
|
@ -18,6 +18,7 @@ class Form::Import
|
||||||
muting: ['Account address', 'Hide notifications'],
|
muting: ['Account address', 'Hide notifications'],
|
||||||
domain_blocking: ['#domain'],
|
domain_blocking: ['#domain'],
|
||||||
bookmarks: ['#uri'],
|
bookmarks: ['#uri'],
|
||||||
|
lists: ['List name', 'Account address'],
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
KNOWN_FIRST_HEADERS = EXPECTED_HEADERS_BY_TYPE.values.map(&:first).uniq.freeze
|
KNOWN_FIRST_HEADERS = EXPECTED_HEADERS_BY_TYPE.values.map(&:first).uniq.freeze
|
||||||
|
@ -30,6 +31,7 @@ class Form::Import
|
||||||
'Hide notifications' => 'hide_notifications',
|
'Hide notifications' => 'hide_notifications',
|
||||||
'#domain' => 'domain',
|
'#domain' => 'domain',
|
||||||
'#uri' => 'uri',
|
'#uri' => 'uri',
|
||||||
|
'List name' => 'list_name',
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
class EmptyFileError < StandardError; end
|
class EmptyFileError < StandardError; end
|
||||||
|
@ -48,6 +50,7 @@ class Form::Import
|
||||||
return :muting if data.original_filename&.start_with?('mutes') || data.original_filename&.start_with?('muted_accounts')
|
return :muting if data.original_filename&.start_with?('mutes') || data.original_filename&.start_with?('muted_accounts')
|
||||||
return :domain_blocking if data.original_filename&.start_with?('domain_blocks') || data.original_filename&.start_with?('blocked_domains')
|
return :domain_blocking if data.original_filename&.start_with?('domain_blocks') || data.original_filename&.start_with?('blocked_domains')
|
||||||
return :bookmarks if data.original_filename&.start_with?('bookmarks')
|
return :bookmarks if data.original_filename&.start_with?('bookmarks')
|
||||||
|
return :lists if data.original_filename&.start_with?('lists')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Whether the uploaded CSV file seems to correspond to a different import type than the one selected
|
# Whether the uploaded CSV file seems to correspond to a different import type than the one selected
|
||||||
|
@ -76,14 +79,16 @@ class Form::Import
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def default_csv_header
|
def default_csv_headers
|
||||||
case type.to_sym
|
case type.to_sym
|
||||||
when :following, :blocking, :muting
|
when :following, :blocking, :muting
|
||||||
'Account address'
|
['Account address']
|
||||||
when :domain_blocking
|
when :domain_blocking
|
||||||
'#domain'
|
['#domain']
|
||||||
when :bookmarks
|
when :bookmarks
|
||||||
'#uri'
|
['#uri']
|
||||||
|
when :lists
|
||||||
|
['List name', 'Account address']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -98,7 +103,7 @@ class Form::Import
|
||||||
field&.split(',')&.map(&:strip)&.presence
|
field&.split(',')&.map(&:strip)&.presence
|
||||||
when 'Account address'
|
when 'Account address'
|
||||||
field.strip.gsub(/\A@/, '')
|
field.strip.gsub(/\A@/, '')
|
||||||
when '#domain', '#uri'
|
when '#domain', '#uri', 'List name'
|
||||||
field.strip
|
field.strip
|
||||||
else
|
else
|
||||||
field
|
field
|
||||||
|
@ -109,7 +114,7 @@ class Form::Import
|
||||||
@csv_data.take(1) # Ensure the headers are read
|
@csv_data.take(1) # Ensure the headers are read
|
||||||
raise EmptyFileError if @csv_data.headers == true
|
raise EmptyFileError if @csv_data.headers == true
|
||||||
|
|
||||||
@csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: [default_csv_header], converters: csv_converter) unless KNOWN_FIRST_HEADERS.include?(@csv_data.headers&.first)
|
@csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: default_csv_headers, converters: csv_converter) unless KNOWN_FIRST_HEADERS.include?(@csv_data.headers&.first)
|
||||||
@csv_data
|
@csv_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -133,7 +138,7 @@ class Form::Import
|
||||||
def validate_data
|
def validate_data
|
||||||
return if data.nil?
|
return if data.nil?
|
||||||
return errors.add(:data, I18n.t('imports.errors.too_large')) if data.size > FILE_SIZE_LIMIT
|
return errors.add(:data, I18n.t('imports.errors.too_large')) if data.size > FILE_SIZE_LIMIT
|
||||||
return errors.add(:data, I18n.t('imports.errors.incompatible_type')) unless csv_data.headers.include?(default_csv_header)
|
return errors.add(:data, I18n.t('imports.errors.incompatible_type')) unless default_csv_headers.all? { |header| csv_data.headers.include?(header) }
|
||||||
|
|
||||||
errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ROWS_PROCESSING_LIMIT)) if csv_row_count > ROWS_PROCESSING_LIMIT
|
errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ROWS_PROCESSING_LIMIT)) if csv_row_count > ROWS_PROCESSING_LIMIT
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ class BulkImportRowService
|
||||||
@type = row.bulk_import.type.to_sym
|
@type = row.bulk_import.type.to_sym
|
||||||
|
|
||||||
case @type
|
case @type
|
||||||
when :following, :blocking, :muting
|
when :following, :blocking, :muting, :lists
|
||||||
target_acct = @data['acct']
|
target_acct = @data['acct']
|
||||||
target_domain = domain(target_acct)
|
target_domain = domain(target_acct)
|
||||||
@target_account = stoplight_wrap_request(target_domain) { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
|
@target_account = stoplight_wrap_request(target_domain) { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
|
||||||
|
@ -33,6 +33,12 @@ class BulkImportRowService
|
||||||
return false unless StatusPolicy.new(@account, @target_status).show?
|
return false unless StatusPolicy.new(@account, @target_status).show?
|
||||||
|
|
||||||
@account.bookmarks.find_or_create_by!(status: @target_status)
|
@account.bookmarks.find_or_create_by!(status: @target_status)
|
||||||
|
when :lists
|
||||||
|
list = @account.owned_lists.find_or_create_by!(title: @data['list_name'])
|
||||||
|
|
||||||
|
FollowService.new.call(@account, @target_account) unless @account.id == @target_account.id
|
||||||
|
|
||||||
|
list.accounts << @target_account
|
||||||
end
|
end
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
|
@ -16,6 +16,8 @@ class BulkImportService < BaseService
|
||||||
import_domain_blocks!
|
import_domain_blocks!
|
||||||
when :bookmarks
|
when :bookmarks
|
||||||
import_bookmarks!
|
import_bookmarks!
|
||||||
|
when :lists
|
||||||
|
import_lists!
|
||||||
end
|
end
|
||||||
|
|
||||||
@import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items
|
@import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items
|
||||||
|
@ -157,4 +159,24 @@ class BulkImportService < BaseService
|
||||||
[row.id]
|
[row.id]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def import_lists!
|
||||||
|
rows = @import.rows.to_a
|
||||||
|
|
||||||
|
if @import.overwrite?
|
||||||
|
included_lists = rows.map { |row| row.data['list_name'] }.uniq
|
||||||
|
|
||||||
|
@account.owned_lists.where.not(title: included_lists).destroy_all
|
||||||
|
|
||||||
|
# As list membership changes do not retroactively change timeline
|
||||||
|
# contents, simplify things by just clearing everything
|
||||||
|
@account.owned_lists.find_each do |list|
|
||||||
|
list.list_accounts.destroy_all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Import::RowWorker.push_bulk(rows) do |row|
|
||||||
|
[row.id]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
- if email_domain_block.parent.present?
|
- if email_domain_block.parent.present?
|
||||||
= t('admin.email_domain_blocks.resolved_through_html', domain: content_tag(:samp, email_domain_block.parent.domain))
|
= t('admin.email_domain_blocks.resolved_through_html', domain: content_tag(:samp, email_domain_block.parent.domain))
|
||||||
•
|
·
|
||||||
|
|
||||||
= t('admin.email_domain_blocks.attempts_over_week', count: email_domain_block.history.reduce(0) { |sum, day| sum + day.accounts })
|
= t('admin.email_domain_blocks.attempts_over_week', count: email_domain_block.history.reduce(0) { |sum, day| sum + day.accounts })
|
||||||
|
|
|
@ -29,11 +29,11 @@
|
||||||
|
|
||||||
%br/
|
%br/
|
||||||
|
|
||||||
= f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ')
|
= f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||||
- if f.object.public_comment.present?
|
- if f.object.public_comment.present?
|
||||||
•
|
·
|
||||||
= f.object.public_comment
|
= f.object.public_comment
|
||||||
- if existing_relationships
|
- if existing_relationships
|
||||||
•
|
·
|
||||||
= fa_icon 'warning fw'
|
= fa_icon 'warning fw'
|
||||||
= t('admin.export_domain_blocks.import.existing_relationships_warning')
|
= t('admin.export_domain_blocks.import.existing_relationships_warning')
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
%small
|
%small
|
||||||
- if instance.domain_block
|
- if instance.domain_block
|
||||||
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ')
|
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||||
- elsif instance.domain_allow
|
- elsif instance.domain_allow
|
||||||
= t('admin.accounts.whitelisted')
|
= t('admin.accounts.whitelisted')
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
%td= @instance.domain_block.public_comment
|
%td= @instance.domain_block.public_comment
|
||||||
%tr
|
%tr
|
||||||
%th= t('admin.instances.content_policies.policy')
|
%th= t('admin.instances.content_policies.policy')
|
||||||
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ')
|
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||||
|
|
||||||
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
|
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
|
||||||
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
.pending-account__header
|
.pending-account__header
|
||||||
%samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}")
|
%samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}")
|
||||||
- if ip_block.comment.present?
|
- if ip_block.comment.present?
|
||||||
•
|
·
|
||||||
= ip_block.comment
|
= ip_block.comment
|
||||||
%br/
|
%br/
|
||||||
= t("simple_form.labels.ip_block.severities.#{ip_block.severity}")
|
= t("simple_form.labels.ip_block.severities.#{ip_block.severity}")
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
= t('admin.roles.everyone_full_description_html')
|
= t('admin.roles.everyone_full_description_html')
|
||||||
- else
|
- else
|
||||||
= link_to t('admin.roles.assigned_users', count: role.users.count), admin_accounts_path(role_ids: role.id)
|
= link_to t('admin.roles.assigned_users', count: role.users.count), admin_accounts_path(role_ids: role.id)
|
||||||
•
|
·
|
||||||
%abbr{ title: role.permissions_as_keys.map { |privilege| I18n.t("admin.roles.privileges.#{privilege}") }.join(', ') }= t('admin.roles.permissions_count', count: role.permissions_as_keys.size)
|
%abbr{ title: role.permissions_as_keys.map { |privilege| I18n.t("admin.roles.privileges.#{privilege}") }.join(', ') }= t('admin.roles.permissions_count', count: role.permissions_as_keys.size)
|
||||||
%div
|
%div
|
||||||
= table_link_to 'pencil', t('admin.accounts.edit'), edit_admin_role_path(role) if can?(:update, role)
|
= table_link_to 'pencil', t('admin.accounts.edit'), edit_admin_role_path(role) if can?(:update, role)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :media_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
= f.input :media_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
||||||
= f.input :content_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
= f.input :content_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }, hint: false, warning_hint: t('simple_form.hints.form_admin_settings.content_cache_retention_period')
|
||||||
= f.input :backups_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
= f.input :backups_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
|
|
|
@ -10,21 +10,21 @@
|
||||||
|
|
||||||
- if preview_card.provider_name.present?
|
- if preview_card.provider_name.present?
|
||||||
= preview_card.provider_name
|
= preview_card.provider_name
|
||||||
•
|
·
|
||||||
|
|
||||||
- if preview_card.language.present?
|
- if preview_card.language.present?
|
||||||
= standard_locale_name(preview_card.language)
|
= standard_locale_name(preview_card.language)
|
||||||
•
|
·
|
||||||
|
|
||||||
= t('admin.trends.links.shared_by_over_week', count: preview_card.history.reduce(0) { |sum, day| sum + day.accounts })
|
= t('admin.trends.links.shared_by_over_week', count: preview_card.history.reduce(0) { |sum, day| sum + day.accounts })
|
||||||
|
|
||||||
- if preview_card.trend.allowed?
|
- if preview_card.trend.allowed?
|
||||||
•
|
·
|
||||||
%abbr{ title: t('admin.trends.tags.current_score', score: preview_card.trend.score) }= t('admin.trends.tags.trending_rank', rank: preview_card.trend.rank)
|
%abbr{ title: t('admin.trends.tags.current_score', score: preview_card.trend.score) }= t('admin.trends.tags.trending_rank', rank: preview_card.trend.rank)
|
||||||
|
|
||||||
- if preview_card.decaying?
|
- if preview_card.decaying?
|
||||||
•
|
·
|
||||||
= t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short))
|
= t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short))
|
||||||
- elsif preview_card.requires_review?
|
- elsif preview_card.requires_review?
|
||||||
•
|
·
|
||||||
= t('admin.trends.pending_review')
|
= t('admin.trends.pending_review')
|
||||||
|
|
|
@ -17,17 +17,17 @@
|
||||||
= t('admin.trends.statuses.shared_by', count: status.reblogs_count + status.favourites_count, friendly_count: friendly_number_to_human(status.reblogs_count + status.favourites_count))
|
= t('admin.trends.statuses.shared_by', count: status.reblogs_count + status.favourites_count, friendly_count: friendly_number_to_human(status.reblogs_count + status.favourites_count))
|
||||||
|
|
||||||
- if status.account.domain.present?
|
- if status.account.domain.present?
|
||||||
•
|
·
|
||||||
= status.account.domain
|
= status.account.domain
|
||||||
- if status.language.present?
|
- if status.language.present?
|
||||||
•
|
·
|
||||||
= standard_locale_name(status.language)
|
= standard_locale_name(status.language)
|
||||||
- if status.trendable? && !status.account.discoverable?
|
- if status.trendable? && !status.account.discoverable?
|
||||||
•
|
·
|
||||||
= t('admin.trends.statuses.not_discoverable')
|
= t('admin.trends.statuses.not_discoverable')
|
||||||
- if status.trend.allowed?
|
- if status.trend.allowed?
|
||||||
•
|
·
|
||||||
%abbr{ title: t('admin.trends.tags.current_score', score: status.trend.score) }= t('admin.trends.tags.trending_rank', rank: status.trend.rank)
|
%abbr{ title: t('admin.trends.tags.current_score', score: status.trend.score) }= t('admin.trends.tags.trending_rank', rank: status.trend.rank)
|
||||||
- elsif status.requires_review?
|
- elsif status.requires_review?
|
||||||
•
|
·
|
||||||
= t('admin.trends.pending_review')
|
= t('admin.trends.pending_review')
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
= t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts })
|
= t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts })
|
||||||
|
|
||||||
- if tag.trendable? && (rank = Trends.tags.rank(tag.id))
|
- if tag.trendable? && (rank = Trends.tags.rank(tag.id))
|
||||||
•
|
·
|
||||||
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
||||||
|
|
||||||
- if tag.decaying?
|
- if tag.decaying?
|
||||||
•
|
·
|
||||||
= t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short))
|
= t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short))
|
||||||
- elsif tag.requires_review?
|
- elsif tag.requires_review?
|
||||||
•
|
·
|
||||||
= t('admin.trends.pending_review')
|
= t('admin.trends.pending_review')
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
- else
|
- else
|
||||||
%span.negative-hint= t('admin.webhooks.disabled')
|
%span.negative-hint= t('admin.webhooks.disabled')
|
||||||
|
|
||||||
•
|
·
|
||||||
|
|
||||||
%abbr{ title: webhook.events.join(', ') }= t('admin.webhooks.enabled_events', count: webhook.events.size)
|
%abbr{ title: webhook.events.join(', ') }= t('admin.webhooks.enabled_events', count: webhook.events.size)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<%= raw t('admin_mailer.new_trends.new_trending_links.title') %>
|
<%= raw t('admin_mailer.new_trends.new_trending_links.title') %>
|
||||||
|
|
||||||
<% @links.each do |link| %>
|
<% @links.each do |link| %>
|
||||||
- <%= link.title %> • <%= link.url %>
|
- <%= link.title %> · <%= link.url %>
|
||||||
<%= standard_locale_name(link.language) %> • <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %>
|
<%= standard_locale_name(link.language) %> · <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= raw t('application_mailer.view')%> <%= admin_trends_links_url %>
|
<%= raw t('application_mailer.view')%> <%= admin_trends_links_url %>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<% @statuses.each do |status| %>
|
<% @statuses.each do |status| %>
|
||||||
- <%= ActivityPub::TagManager.instance.url_for(status) %>
|
- <%= ActivityPub::TagManager.instance.url_for(status) %>
|
||||||
<%= standard_locale_name(status.language) %> • <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %>
|
<%= standard_locale_name(status.language) %> · <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= raw t('application_mailer.view')%> <%= admin_trends_statuses_url %>
|
<%= raw t('application_mailer.view')%> <%= admin_trends_statuses_url %>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<% @tags.each do |tag| %>
|
<% @tags.each do |tag| %>
|
||||||
- #<%= tag.display_name %>
|
- #<%= tag.display_name %>
|
||||||
<%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %>
|
<%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @lowest_trending_tag %>
|
<% if @lowest_trending_tag %>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
- account_url = local_assigns[:admin] ? admin_account_path(account.id) : ActivityPub::TagManager.instance.url_for(account)
|
- account_url = local_assigns[:admin] ? admin_account_path(account.id) : ActivityPub::TagManager.instance.url_for(account)
|
||||||
|
- compact ||= false
|
||||||
|
|
||||||
.card.h-card
|
.card.h-card
|
||||||
= link_to account_url, target: '_blank', rel: 'noopener noreferrer' do
|
= link_to account_url, target: '_blank', rel: 'noopener noreferrer' do
|
||||||
|
- unless compact
|
||||||
.card__img
|
.card__img
|
||||||
= image_tag account.header.url, alt: ''
|
= image_tag account.header.url, alt: ''
|
||||||
.card__bar
|
.card__bar
|
||||||
|
|
|
@ -7,6 +7,12 @@
|
||||||
.simple_form
|
.simple_form
|
||||||
= render 'auth/shared/progress', stage: 'rules'
|
= render 'auth/shared/progress', stage: 'rules'
|
||||||
|
|
||||||
|
- if @invite.present? && @invite.autofollow?
|
||||||
|
%h1.title= t('auth.rules.title_invited')
|
||||||
|
%p.lead.invited-by= t('auth.rules.invited_by', domain: site_hostname)
|
||||||
|
= render 'application/card', account: @invite.user.account, compact: true
|
||||||
|
%p.lead= t('auth.rules.preamble_invited', domain: site_hostname)
|
||||||
|
- else
|
||||||
%h1.title= t('auth.rules.title')
|
%h1.title= t('auth.rules.title')
|
||||||
%p.lead= t('auth.rules.preamble', domain: site_hostname)
|
%p.lead= t('auth.rules.preamble', domain: site_hostname)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
- else
|
- else
|
||||||
= t('doorkeeper.authorized_applications.index.never_used')
|
= t('doorkeeper.authorized_applications.index.never_used')
|
||||||
|
|
||||||
•
|
·
|
||||||
|
|
||||||
= t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date))
|
= t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date))
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
= simple_form_for @import, url: settings_imports_path do |f|
|
= simple_form_for @import, url: settings_imports_path do |f|
|
||||||
.field-group
|
.field-group
|
||||||
= f.input :type, as: :grouped_select, collection: { constructive: %i(following bookmarks), destructive: %i(muting blocking domain_blocking) }, wrapper: :with_block_label, include_blank: false, label_method: ->(type) { I18n.t("imports.types.#{type}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_method: :last, hint: t('imports.preface')
|
= f.input :type, as: :grouped_select, collection: { constructive: %i(following bookmarks lists), destructive: %i(muting blocking domain_blocking) }, wrapper: :with_block_label, include_blank: false, label_method: ->(type) { I18n.t("imports.types.#{type}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_method: :last, hint: t('imports.preface')
|
||||||
|
|
||||||
.fields-row
|
.fields-row
|
||||||
.fields-group.fields-row__column.fields-row__column-6
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
|
|
|
@ -30,9 +30,18 @@ module KmyblueComponent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module WarningHintComponent
|
||||||
|
def warning_hint(_wrapper_options = nil)
|
||||||
|
@warning_hint ||= begin
|
||||||
|
options[:warning_hint].to_s.html_safe if options[:warning_hint].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
SimpleForm.include_component(AppendComponent)
|
SimpleForm.include_component(AppendComponent)
|
||||||
SimpleForm.include_component(RecommendedComponent)
|
SimpleForm.include_component(RecommendedComponent)
|
||||||
SimpleForm.include_component(KmyblueComponent)
|
SimpleForm.include_component(KmyblueComponent)
|
||||||
|
SimpleForm.include_component(WarningHintComponent)
|
||||||
|
|
||||||
SimpleForm.setup do |config|
|
SimpleForm.setup do |config|
|
||||||
# Wrappers are used by the form builder to generate a
|
# Wrappers are used by the form builder to generate a
|
||||||
|
@ -114,6 +123,7 @@ SimpleForm.setup do |config|
|
||||||
b.use :html5
|
b.use :html5
|
||||||
b.use :label
|
b.use :label
|
||||||
b.use :hint, wrap_with: { tag: :span, class: :hint }
|
b.use :hint, wrap_with: { tag: :span, class: :hint }
|
||||||
|
b.use :warning_hint, wrap_with: { tag: :span, class: [:hint, 'warning-hint'] }
|
||||||
b.use :input, wrap_with: { tag: :div, class: :label_input }
|
b.use :input, wrap_with: { tag: :div, class: :label_input }
|
||||||
b.use :error, wrap_with: { tag: :span, class: :error }
|
b.use :error, wrap_with: { tag: :span, class: :error }
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,7 +25,7 @@ module Twitter::TwitterText
|
||||||
\)
|
\)
|
||||||
/iox
|
/iox
|
||||||
UCHARS = '\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}'
|
UCHARS = '\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}'
|
||||||
REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@#{UCHARS}]/iou
|
REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@\^#{UCHARS}]/iou
|
||||||
REGEXEN[:valid_url_query_ending_chars] = /[a-z0-9_&=#\/\-#{UCHARS}]/iou
|
REGEXEN[:valid_url_query_ending_chars] = /[a-z0-9_&=#\/\-#{UCHARS}]/iou
|
||||||
REGEXEN[:valid_url_path] = /(?:
|
REGEXEN[:valid_url_path] = /(?:
|
||||||
(?:
|
(?:
|
||||||
|
|
|
@ -1126,8 +1126,11 @@ en:
|
||||||
rules:
|
rules:
|
||||||
accept: Accept
|
accept: Accept
|
||||||
back: Back
|
back: Back
|
||||||
|
invited_by: 'You can join %{domain} thanks to the invitation you have received from:'
|
||||||
preamble: These are set and enforced by the %{domain} moderators.
|
preamble: These are set and enforced by the %{domain} moderators.
|
||||||
|
preamble_invited: Before you proceed, please consider the ground rules set by the moderators of %{domain}.
|
||||||
title: Some ground rules.
|
title: Some ground rules.
|
||||||
|
title_invited: You've been invited.
|
||||||
security: Security
|
security: Security
|
||||||
set_new_password: Set new password
|
set_new_password: Set new password
|
||||||
setup:
|
setup:
|
||||||
|
|
|
@ -82,7 +82,7 @@ en:
|
||||||
backups_retention_period: Keep generated user archives for the specified number of days.
|
backups_retention_period: Keep generated user archives for the specified number of days.
|
||||||
bootstrap_timeline_accounts: These accounts will be pinned to the top of new users' follow recommendations.
|
bootstrap_timeline_accounts: These accounts will be pinned to the top of new users' follow recommendations.
|
||||||
closed_registrations_message: Displayed when sign-ups are closed
|
closed_registrations_message: Displayed when sign-ups are closed
|
||||||
content_cache_retention_period: Posts from other servers will be deleted after the specified number of days when set to a positive value. This may be irreversible.
|
content_cache_retention_period: All posts and boosts from other servers will be deleted after the specified number of days. Some posts may not be recoverable. All related bookmarks, favourites and boosts will also be lost and impossible to undo.
|
||||||
custom_css: You can apply custom styles on the web version of Mastodon.
|
custom_css: You can apply custom styles on the web version of Mastodon.
|
||||||
mascot: Overrides the illustration in the advanced web interface.
|
mascot: Overrides the illustration in the advanced web interface.
|
||||||
media_cache_retention_period: Downloaded media files will be deleted after the specified number of days when set to a positive value, and re-downloaded on demand.
|
media_cache_retention_period: Downloaded media files will be deleted after the specified number of days when set to a positive value, and re-downloaded on demand.
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
console.error("The localisation functionality has been refactored, please see the Localisation section in the development documentation (https://docs.joinmastodon.org/dev/code/#localizations)");
|
|
||||||
|
|
||||||
process.exit(1);
|
|
|
@ -13,7 +13,6 @@ const config = {
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
'app/javascript/mastodon/**/*.{js,jsx,ts,tsx}',
|
'app/javascript/mastodon/**/*.{js,jsx,ts,tsx}',
|
||||||
'!app/javascript/mastodon/features/emoji/emoji_compressed.js',
|
'!app/javascript/mastodon/features/emoji/emoji_compressed.js',
|
||||||
'!app/javascript/mastodon/locales/locale-data/*.js',
|
|
||||||
'!app/javascript/mastodon/service_worker/entry.js',
|
'!app/javascript/mastodon/service_worker/entry.js',
|
||||||
'!app/javascript/mastodon/test_setup.js',
|
'!app/javascript/mastodon/test_setup.js',
|
||||||
],
|
],
|
||||||
|
|
26
lib/linter/haml_middle_dot.rb
Normal file
26
lib/linter/haml_middle_dot.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module HamlLint
|
||||||
|
# Bans the usage of “•” (bullet) in HTML/HAML in favor of “·” (middle dot) in anything that will end up as a text node. (including string literals in Ruby code)
|
||||||
|
class Linter::MiddleDot < Linter
|
||||||
|
include LinterRegistry
|
||||||
|
|
||||||
|
# rubocop:disable Style/MiddleDot
|
||||||
|
BULLET = '•'
|
||||||
|
# rubocop:enable Style/MiddleDot
|
||||||
|
MIDDLE_DOT = '·'
|
||||||
|
MESSAGE = "Use '#{MIDDLE_DOT}' (middle dot) instead of '#{BULLET}' (bullet)".freeze
|
||||||
|
|
||||||
|
def visit_plain(node)
|
||||||
|
return unless node.text.include?(BULLET)
|
||||||
|
|
||||||
|
record_lint(node, MESSAGE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def visit_script(node)
|
||||||
|
return unless node.script.include?(BULLET)
|
||||||
|
|
||||||
|
record_lint(node, MESSAGE)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
31
lib/linter/rubocop_middle_dot.rb
Normal file
31
lib/linter/rubocop_middle_dot.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Style
|
||||||
|
# Bans the usage of “•” (bullet) in HTML/HAML in favor of “·” (middle dot) in string literals
|
||||||
|
class MiddleDot < Base
|
||||||
|
extend AutoCorrector
|
||||||
|
extend Util
|
||||||
|
|
||||||
|
# rubocop:disable Style/MiddleDot
|
||||||
|
BULLET = '•'
|
||||||
|
# rubocop:enable Style/MiddleDot
|
||||||
|
MIDDLE_DOT = '·'
|
||||||
|
MESSAGE = "Use '#{MIDDLE_DOT}' (middle dot) instead of '#{BULLET}' (bullet)".freeze
|
||||||
|
|
||||||
|
def on_str(node)
|
||||||
|
# Constants like __FILE__ are handled as strings,
|
||||||
|
# but don't respond to begin.
|
||||||
|
return unless node.loc.respond_to?(:begin) && node.loc.begin
|
||||||
|
|
||||||
|
return unless node.value.include?(BULLET)
|
||||||
|
|
||||||
|
add_offense(node, message: MESSAGE) do |corrector|
|
||||||
|
corrector.replace(node, node.source.gsub(BULLET, MIDDLE_DOT))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,16 +4,39 @@ require_relative '../../../config/boot'
|
||||||
require_relative '../../../config/environment'
|
require_relative '../../../config/environment'
|
||||||
|
|
||||||
require 'thor'
|
require 'thor'
|
||||||
require_relative 'helper'
|
require_relative 'progress_helper'
|
||||||
|
|
||||||
module Mastodon
|
module Mastodon
|
||||||
module CLI
|
module CLI
|
||||||
class Base < Thor
|
class Base < Thor
|
||||||
include CLI::Helper
|
include ProgressHelper
|
||||||
|
|
||||||
def self.exit_on_failure?
|
def self.exit_on_failure?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def pastel
|
||||||
|
@pastel ||= Pastel.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def dry_run?
|
||||||
|
options[:dry_run]
|
||||||
|
end
|
||||||
|
|
||||||
|
def dry_run_mode_suffix
|
||||||
|
dry_run? ? ' (DRY RUN)' : ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_connection_pools!
|
||||||
|
ActiveRecord::Base.establish_connection(
|
||||||
|
ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first.configuration_hash
|
||||||
|
.dup
|
||||||
|
.tap { |config| config['pool'] = options[:concurrency] + 1 }
|
||||||
|
)
|
||||||
|
RedisConfiguration.establish_pool(options[:concurrency])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,23 +9,19 @@ HttpLog.configuration.logger = dev_null
|
||||||
Paperclip.options[:log] = false
|
Paperclip.options[:log] = false
|
||||||
Chewy.logger = dev_null
|
Chewy.logger = dev_null
|
||||||
|
|
||||||
module Mastodon::CLI
|
require 'ruby-progressbar/outputs/null'
|
||||||
module Helper
|
|
||||||
def dry_run?
|
|
||||||
options[:dry_run]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dry_run_mode_suffix
|
module Mastodon::CLI
|
||||||
dry_run? ? ' (DRY RUN)' : ''
|
module ProgressHelper
|
||||||
end
|
PROGRESS_FORMAT = '%c/%u |%b%i| %e'
|
||||||
|
|
||||||
def create_progress_bar(total = nil)
|
def create_progress_bar(total = nil)
|
||||||
ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
|
ProgressBar.create(
|
||||||
end
|
{
|
||||||
|
total: total,
|
||||||
def reset_connection_pools!
|
format: PROGRESS_FORMAT,
|
||||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env].dup.tap { |config| config['pool'] = options[:concurrency] + 1 })
|
}.merge(progress_output_options)
|
||||||
RedisConfiguration.establish_pool(options[:concurrency])
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def parallelize_with_progress(scope)
|
def parallelize_with_progress(scope)
|
||||||
|
@ -82,8 +78,10 @@ module Mastodon::CLI
|
||||||
[total.value, aggregate.value]
|
[total.value, aggregate.value]
|
||||||
end
|
end
|
||||||
|
|
||||||
def pastel
|
private
|
||||||
@pastel ||= Pastel.new
|
|
||||||
|
def progress_output_options
|
||||||
|
Rails.env.test? ? { output: ProgressBar::Outputs::Null } : {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -29,15 +29,7 @@ module Mastodon::CLI
|
||||||
database will be imported into the indices, unless overridden with --no-import.
|
database will be imported into the indices, unless overridden with --no-import.
|
||||||
LONG_DESC
|
LONG_DESC
|
||||||
def deploy
|
def deploy
|
||||||
if options[:concurrency] < 1
|
verify_deploy_options!
|
||||||
say('Cannot run with this concurrency setting, must be at least 1', :red)
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if options[:batch_size] < 1
|
|
||||||
say('Cannot run with this batch_size setting, must be at least 1', :red)
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
indices = if options[:only]
|
indices = if options[:only]
|
||||||
options[:only].map { |str| "#{str.camelize}Index".constantize }
|
options[:only].map { |str| "#{str.camelize}Index".constantize }
|
||||||
|
@ -98,5 +90,26 @@ module Mastodon::CLI
|
||||||
|
|
||||||
say("Indexed #{added} records, de-indexed #{removed}", :green, true)
|
say("Indexed #{added} records, de-indexed #{removed}", :green, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def verify_deploy_options!
|
||||||
|
verify_deploy_concurrency!
|
||||||
|
verify_deploy_batch_size!
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_deploy_concurrency!
|
||||||
|
return unless options[:concurrency] < 1
|
||||||
|
|
||||||
|
say('Cannot run with this concurrency setting, must be at least 1', :red)
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_deploy_batch_size!
|
||||||
|
return unless options[:batch_size] < 1
|
||||||
|
|
||||||
|
say('Cannot run with this batch_size setting, must be at least 1', :red)
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
14
package.json
14
package.json
|
@ -21,7 +21,6 @@
|
||||||
"lint:sass": "stylelint \"**/*.{css,scss}\" && prettier --check \"**/*.{css,scss}\"",
|
"lint:sass": "stylelint \"**/*.{css,scss}\" && prettier --check \"**/*.{css,scss}\"",
|
||||||
"lint:yml": "prettier --check \"**/*.{yaml,yml}\"",
|
"lint:yml": "prettier --check \"**/*.{yaml,yml}\"",
|
||||||
"lint": "yarn lint:js && yarn lint:json && yarn lint:sass && yarn lint:yml",
|
"lint": "yarn lint:js && yarn lint:json && yarn lint:sass && yarn lint:yml",
|
||||||
"manage:translations": "node ./config/webpack/translationRunner.js",
|
|
||||||
"postversion": "git push --tags",
|
"postversion": "git push --tags",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"start": "node ./streaming/index.js",
|
"start": "node ./streaming/index.js",
|
||||||
|
@ -49,6 +48,7 @@
|
||||||
"@reduxjs/toolkit": "^1.9.5",
|
"@reduxjs/toolkit": "^1.9.5",
|
||||||
"abortcontroller-polyfill": "^1.7.5",
|
"abortcontroller-polyfill": "^1.7.5",
|
||||||
"arrow-key-navigation": "^1.2.0",
|
"arrow-key-navigation": "^1.2.0",
|
||||||
|
"async-mutex": "^0.4.0",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"babel-loader": "^8.3.0",
|
"babel-loader": "^8.3.0",
|
||||||
|
@ -138,12 +138,12 @@
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^3.3.12",
|
||||||
"webpack-merge": "^5.9.0",
|
"webpack-merge": "^5.9.0",
|
||||||
"wicg-inert": "^3.1.2",
|
"wicg-inert": "^3.1.2",
|
||||||
"workbox-expiration": "^6.6.0",
|
"workbox-expiration": "^7.0.0",
|
||||||
"workbox-precaching": "^6.6.0",
|
"workbox-precaching": "^7.0.0",
|
||||||
"workbox-routing": "^6.6.0",
|
"workbox-routing": "^7.0.0",
|
||||||
"workbox-strategies": "^6.6.0",
|
"workbox-strategies": "^7.0.0",
|
||||||
"workbox-webpack-plugin": "^6.6.0",
|
"workbox-webpack-plugin": "^7.0.0",
|
||||||
"workbox-window": "^6.6.0",
|
"workbox-window": "^7.0.0",
|
||||||
"ws": "^8.12.1"
|
"ws": "^8.12.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
3
spec/fixtures/files/lists.csv
vendored
Normal file
3
spec/fixtures/files/lists.csv
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Mastodon project,gargron@example.com
|
||||||
|
Mastodon project,mastodon@example.com
|
||||||
|
test,foo@example.com
|
|
|
@ -33,7 +33,7 @@ describe ReactComponentHelper do
|
||||||
|
|
||||||
it 'returns a tag with data attributes' do
|
it 'returns a tag with data attributes' do
|
||||||
expect(parsed_html.div['data-admin-component']).to eq('Name')
|
expect(parsed_html.div['data-admin-component']).to eq('Name')
|
||||||
expect(parsed_html.div['data-props']).to eq('{"locale":"en","one":"two"}')
|
expect(parsed_html.div['data-props']).to eq('{"one":"two"}')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -924,4 +924,78 @@ describe Mastodon::CLI::Accounts do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#rotate' do
|
||||||
|
context 'when neither username nor --all option are given' do
|
||||||
|
it 'exits with an error message' do
|
||||||
|
expect { cli.rotate }.to output(
|
||||||
|
a_string_including('No account(s) given')
|
||||||
|
).to_stdout
|
||||||
|
.and raise_error(SystemExit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a username is given' do
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
it 'correctly rotates keys for the specified account' do
|
||||||
|
old_private_key = account.private_key
|
||||||
|
old_public_key = account.public_key
|
||||||
|
|
||||||
|
cli.rotate(account.username)
|
||||||
|
account.reload
|
||||||
|
|
||||||
|
expect(account.private_key).to_not eq(old_private_key)
|
||||||
|
expect(account.public_key).to_not eq(old_public_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'broadcasts the new keys for the specified account' do
|
||||||
|
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
|
||||||
|
|
||||||
|
cli.rotate(account.username)
|
||||||
|
|
||||||
|
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the given username is not found' do
|
||||||
|
it 'exits with an error message when the specified username is not found' do
|
||||||
|
expect { cli.rotate('non_existent_username') }.to output(
|
||||||
|
a_string_including('No such account')
|
||||||
|
).to_stdout
|
||||||
|
.and raise_error(SystemExit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when --all option is provided' do
|
||||||
|
let(:accounts) { Fabricate.times(3, :account) }
|
||||||
|
let(:options) { { all: true } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Account).to receive(:local).and_return(Account.where(id: accounts.map(&:id)))
|
||||||
|
cli.options = { all: true }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'correctly rotates keys for all local accounts' do
|
||||||
|
old_private_keys = accounts.map(&:private_key)
|
||||||
|
old_public_keys = accounts.map(&:public_key)
|
||||||
|
|
||||||
|
cli.rotate
|
||||||
|
accounts.each(&:reload)
|
||||||
|
|
||||||
|
expect(accounts.map(&:private_key)).to_not eq(old_private_keys)
|
||||||
|
expect(accounts.map(&:public_key)).to_not eq(old_public_keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'broadcasts the new keys for each account' do
|
||||||
|
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
|
||||||
|
|
||||||
|
cli.rotate
|
||||||
|
|
||||||
|
accounts.each do |account|
|
||||||
|
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,6 +86,7 @@ RSpec.describe Form::Import do
|
||||||
it_behaves_like 'too many CSV rows', 'muting', 'imports.txt', 1
|
it_behaves_like 'too many CSV rows', 'muting', 'imports.txt', 1
|
||||||
it_behaves_like 'too many CSV rows', 'domain_blocking', 'domain_blocks.csv', 2
|
it_behaves_like 'too many CSV rows', 'domain_blocking', 'domain_blocks.csv', 2
|
||||||
it_behaves_like 'too many CSV rows', 'bookmarks', 'bookmark-imports.txt', 3
|
it_behaves_like 'too many CSV rows', 'bookmarks', 'bookmark-imports.txt', 3
|
||||||
|
it_behaves_like 'too many CSV rows', 'lists', 'lists.csv', 2
|
||||||
|
|
||||||
# Importing list of addresses with no headers into various types
|
# Importing list of addresses with no headers into various types
|
||||||
it_behaves_like 'valid import', 'following', 'imports.txt'
|
it_behaves_like 'valid import', 'following', 'imports.txt'
|
||||||
|
@ -98,6 +99,9 @@ RSpec.describe Form::Import do
|
||||||
# Importing bookmarks list with no headers into expected type
|
# Importing bookmarks list with no headers into expected type
|
||||||
it_behaves_like 'valid import', 'bookmarks', 'bookmark-imports.txt'
|
it_behaves_like 'valid import', 'bookmarks', 'bookmark-imports.txt'
|
||||||
|
|
||||||
|
# Importing lists with no headers into expected type
|
||||||
|
it_behaves_like 'valid import', 'lists', 'lists.csv'
|
||||||
|
|
||||||
# Importing followed accounts with headers into various compatible types
|
# Importing followed accounts with headers into various compatible types
|
||||||
it_behaves_like 'valid import', 'following', 'following_accounts.csv'
|
it_behaves_like 'valid import', 'following', 'following_accounts.csv'
|
||||||
it_behaves_like 'valid import', 'blocking', 'following_accounts.csv'
|
it_behaves_like 'valid import', 'blocking', 'following_accounts.csv'
|
||||||
|
@ -273,6 +277,12 @@ RSpec.describe Form::Import do
|
||||||
{ 'acct' => 'user@test.com', 'hide_notifications' => false },
|
{ 'acct' => 'user@test.com', 'hide_notifications' => false },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
it_behaves_like 'on successful import', 'lists', 'merge', 'lists.csv', [
|
||||||
|
{ 'acct' => 'gargron@example.com', 'list_name' => 'Mastodon project' },
|
||||||
|
{ 'acct' => 'mastodon@example.com', 'list_name' => 'Mastodon project' },
|
||||||
|
{ 'acct' => 'foo@example.com', 'list_name' => 'test' },
|
||||||
|
]
|
||||||
|
|
||||||
# Based on the bug report 20571 where UTF-8 encoded domains were rejecting import of their users
|
# Based on the bug report 20571 where UTF-8 encoded domains were rejecting import of their users
|
||||||
#
|
#
|
||||||
# https://github.com/mastodon/mastodon/issues/20571
|
# https://github.com/mastodon/mastodon/issues/20571
|
||||||
|
|
|
@ -91,5 +91,77 @@ RSpec.describe BulkImportRowService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when importing a list row' do
|
||||||
|
let(:import_type) { 'lists' }
|
||||||
|
let(:target_account) { Fabricate(:account) }
|
||||||
|
let(:data) do
|
||||||
|
{ 'acct' => target_account.acct, 'list_name' => 'my list' }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'common behavior' do
|
||||||
|
context 'when the target account is already followed' do
|
||||||
|
before do
|
||||||
|
account.follow!(target_account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
expect(subject.call(import_row)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the target account to the list' do
|
||||||
|
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user already requested to follow the target account' do
|
||||||
|
before do
|
||||||
|
account.request_follow!(target_account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
expect(subject.call(import_row)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the target account to the list' do
|
||||||
|
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the target account is neither followed nor requested' do
|
||||||
|
it 'returns true' do
|
||||||
|
expect(subject.call(import_row)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the target account to the list' do
|
||||||
|
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the target account is the user themself' do
|
||||||
|
let(:target_account) { account }
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
expect(subject.call(import_row)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the target account to the list' do
|
||||||
|
expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the list does not exist yet' do
|
||||||
|
include_examples 'common behavior'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the list exists' do
|
||||||
|
before do
|
||||||
|
Fabricate(:list, account: account, title: 'my list')
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples 'common behavior'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ RSpec.describe FetchLinkCardService, type: :service do
|
||||||
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
|
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
|
||||||
stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt'))
|
stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt'))
|
||||||
stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404)
|
stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404)
|
||||||
|
stub_request(:get, 'http://example.com/test?data=file.gpx%5E1').to_return(status: 200)
|
||||||
stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt'))
|
stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt'))
|
||||||
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
|
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
|
||||||
|
|
||||||
|
@ -87,6 +88,15 @@ RSpec.describe FetchLinkCardService, type: :service do
|
||||||
expect(a_request(:get, 'http://example.com/sjis')).to_not have_been_made
|
expect(a_request(:get, 'http://example.com/sjis')).to_not have_been_made
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context do
|
||||||
|
let(:status) { Fabricate(:status, text: 'test http://example.com/test?data=file.gpx^1') }
|
||||||
|
|
||||||
|
it 'does fetch URLs with a caret in search params' do
|
||||||
|
expect(a_request(:get, 'http://example.com/test?data=file.gpx')).to_not have_been_made
|
||||||
|
expect(a_request(:get, 'http://example.com/test?data=file.gpx%5E1')).to have_been_made.once
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a remote status' do
|
context 'with a remote status' do
|
||||||
|
|
274
yarn.lock
274
yarn.lock
|
@ -1743,6 +1743,15 @@
|
||||||
"@jridgewell/set-array" "^1.0.0"
|
"@jridgewell/set-array" "^1.0.0"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.3.0":
|
||||||
|
version "0.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
|
||||||
|
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.0.1"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/gen-mapping@^0.3.2":
|
"@jridgewell/gen-mapping@^0.3.2":
|
||||||
version "0.3.2"
|
version "0.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
|
||||||
|
@ -1767,6 +1776,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
||||||
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
||||||
|
|
||||||
|
"@jridgewell/source-map@^0.3.2":
|
||||||
|
version "0.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
|
||||||
|
integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@1.4.14":
|
"@jridgewell/sourcemap-codec@1.4.14":
|
||||||
version "1.4.14"
|
version "1.4.14"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||||
|
@ -3017,9 +3034,9 @@ ansi-regex@^2.0.0:
|
||||||
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
||||||
|
|
||||||
ansi-regex@^4.1.0:
|
ansi-regex@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
|
||||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
|
||||||
|
|
||||||
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
|
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
|
@ -3266,6 +3283,13 @@ async-limiter@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||||
|
|
||||||
|
async-mutex@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f"
|
||||||
|
integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
async@^2.6.2:
|
async@^2.6.2:
|
||||||
version "2.6.4"
|
version "2.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
|
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
|
||||||
|
@ -7862,9 +7886,9 @@ loader-utils@^1.2.3, loader-utils@^1.4.0:
|
||||||
json5 "^1.0.1"
|
json5 "^1.0.1"
|
||||||
|
|
||||||
loader-utils@^2.0.0:
|
loader-utils@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
|
||||||
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
|
integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
|
||||||
dependencies:
|
dependencies:
|
||||||
big.js "^5.2.2"
|
big.js "^5.2.2"
|
||||||
emojis-list "^3.0.0"
|
emojis-list "^3.0.0"
|
||||||
|
@ -10819,7 +10843,7 @@ source-map@^0.7.3:
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||||
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
||||||
|
|
||||||
source-map@^0.8.0-beta.0, source-map@~0.8.0-beta.0:
|
source-map@^0.8.0-beta.0:
|
||||||
version "0.8.0-beta.0"
|
version "0.8.0-beta.0"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
|
||||||
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
|
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
|
||||||
|
@ -10900,9 +10924,9 @@ sprintf-js@~1.0.2:
|
||||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||||
|
|
||||||
ssri@^8.0.0:
|
ssri@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
|
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
|
||||||
integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
|
integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass "^3.1.1"
|
minipass "^3.1.1"
|
||||||
|
|
||||||
|
@ -11396,13 +11420,13 @@ terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^4.2.3:
|
||||||
webpack-sources "^1.4.3"
|
webpack-sources "^1.4.3"
|
||||||
|
|
||||||
terser@^5.0.0, terser@^5.3.4:
|
terser@^5.0.0, terser@^5.3.4:
|
||||||
version "5.13.1"
|
version "5.17.6"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.6.tgz#d810e75e1bb3350c799cd90ebefe19c9412c12de"
|
||||||
integrity sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==
|
integrity sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@jridgewell/source-map" "^0.3.2"
|
||||||
acorn "^8.5.0"
|
acorn "^8.5.0"
|
||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map "~0.8.0-beta.0"
|
|
||||||
source-map-support "~0.5.20"
|
source-map-support "~0.5.20"
|
||||||
|
|
||||||
tesseract.js-core@^2.2.0:
|
tesseract.js-core@^2.2.0:
|
||||||
|
@ -12314,25 +12338,25 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
|
||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||||
|
|
||||||
workbox-background-sync@6.6.1:
|
workbox-background-sync@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f"
|
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz#2b84b96ca35fec976e3bd2794b70e4acec46b3a5"
|
||||||
integrity sha512-trJd3ovpWCvzu4sW0E8rV3FUyIcC0W8G+AZ+VcqzzA890AsWZlUGOTSxIMmIHVusUw/FDq1HFWfy/kC/WTRqSg==
|
integrity sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==
|
||||||
dependencies:
|
dependencies:
|
||||||
idb "^7.0.1"
|
idb "^7.0.1"
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-broadcast-update@6.6.1:
|
workbox-broadcast-update@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e"
|
resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-7.0.0.tgz#7f611ca1a94ba8ac0aa40fa171c9713e0f937d22"
|
||||||
integrity sha512-fBhffRdaANdeQ1V8s692R9l/gzvjjRtydBOvR6WCSB0BNE2BacA29Z4r9/RHd9KaXCPl6JTdI9q0bR25YKP8TQ==
|
integrity sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-build@6.6.1:
|
workbox-build@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0"
|
resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-7.0.0.tgz#02ab5ef2991b3369b8b9395703f08912212769b4"
|
||||||
integrity sha512-INPgDx6aRycAugUixbKgiEQBWD0MPZqU5r0jyr24CehvNuLPSXp/wGOpdRJmts656lNiXwqV7dC2nzyrzWEDnw==
|
integrity sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@apideck/better-ajv-errors" "^0.3.1"
|
"@apideck/better-ajv-errors" "^0.3.1"
|
||||||
"@babel/core" "^7.11.1"
|
"@babel/core" "^7.11.1"
|
||||||
|
@ -12356,132 +12380,132 @@ workbox-build@6.6.1:
|
||||||
strip-comments "^2.0.1"
|
strip-comments "^2.0.1"
|
||||||
tempy "^0.6.0"
|
tempy "^0.6.0"
|
||||||
upath "^1.2.0"
|
upath "^1.2.0"
|
||||||
workbox-background-sync "6.6.1"
|
workbox-background-sync "7.0.0"
|
||||||
workbox-broadcast-update "6.6.1"
|
workbox-broadcast-update "7.0.0"
|
||||||
workbox-cacheable-response "6.6.1"
|
workbox-cacheable-response "7.0.0"
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
workbox-expiration "6.6.1"
|
workbox-expiration "7.0.0"
|
||||||
workbox-google-analytics "6.6.1"
|
workbox-google-analytics "7.0.0"
|
||||||
workbox-navigation-preload "6.6.1"
|
workbox-navigation-preload "7.0.0"
|
||||||
workbox-precaching "6.6.1"
|
workbox-precaching "7.0.0"
|
||||||
workbox-range-requests "6.6.1"
|
workbox-range-requests "7.0.0"
|
||||||
workbox-recipes "6.6.1"
|
workbox-recipes "7.0.0"
|
||||||
workbox-routing "6.6.1"
|
workbox-routing "7.0.0"
|
||||||
workbox-strategies "6.6.1"
|
workbox-strategies "7.0.0"
|
||||||
workbox-streams "6.6.1"
|
workbox-streams "7.0.0"
|
||||||
workbox-sw "6.6.1"
|
workbox-sw "7.0.0"
|
||||||
workbox-window "6.6.1"
|
workbox-window "7.0.0"
|
||||||
|
|
||||||
workbox-cacheable-response@6.6.1:
|
workbox-cacheable-response@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9"
|
resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz#ee27c036728189eed69d25a135013053277482d2"
|
||||||
integrity sha512-85LY4veT2CnTCDxaVG7ft3NKaFbH6i4urZXgLiU4AiwvKqS2ChL6/eILiGRYXfZ6gAwDnh5RkuDbr/GMS4KSag==
|
integrity sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-core@6.6.1:
|
workbox-core@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265"
|
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.0.0.tgz#dec114ec923cc2adc967dd9be1b8a0bed50a3545"
|
||||||
integrity sha512-ZrGBXjjaJLqzVothoE12qTbVnOAjFrHDXpZe7coCb6q65qI/59rDLwuFMO4PcZ7jcbxY+0+NhUVztzR/CbjEFw==
|
integrity sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==
|
||||||
|
|
||||||
workbox-expiration@6.6.1, workbox-expiration@^6.6.0:
|
workbox-expiration@7.0.0, workbox-expiration@^7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739"
|
resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-7.0.0.tgz#3d90bcf2a7577241de950f89784f6546b66c2baa"
|
||||||
integrity sha512-qFiNeeINndiOxaCrd2DeL1Xh1RFug3JonzjxUHc5WkvkD2u5abY3gZL1xSUNt3vZKsFFGGORItSjVTVnWAZO4A==
|
integrity sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
idb "^7.0.1"
|
idb "^7.0.1"
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-google-analytics@6.6.1:
|
workbox-google-analytics@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d"
|
resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-7.0.0.tgz#603b2c4244af1e85de0fb26287d4e17d3293452a"
|
||||||
integrity sha512-1TjSvbFSLmkpqLcBsF7FuGqqeDsf+uAXO/pjiINQKg3b1GN0nBngnxLcXDYo1n/XxK4N7RaRrpRlkwjY/3ocuA==
|
integrity sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-background-sync "6.6.1"
|
workbox-background-sync "7.0.0"
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
workbox-routing "6.6.1"
|
workbox-routing "7.0.0"
|
||||||
workbox-strategies "6.6.1"
|
workbox-strategies "7.0.0"
|
||||||
|
|
||||||
workbox-navigation-preload@6.6.1:
|
workbox-navigation-preload@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059"
|
resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-7.0.0.tgz#4913878dbbd97057181d57baa18d2bbdde085c6c"
|
||||||
integrity sha512-DQCZowCecO+wRoIxJI2V6bXWK6/53ff+hEXLGlQL4Rp9ZaPDLrgV/32nxwWIP7QpWDkVEtllTAK5h6cnhxNxDA==
|
integrity sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-precaching@6.6.1, workbox-precaching@^6.6.0:
|
workbox-precaching@7.0.0, workbox-precaching@^7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2"
|
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.0.0.tgz#3979ba8033aadf3144b70e9fe631d870d5fbaa03"
|
||||||
integrity sha512-K4znSJ7IKxCnCYEdhNkMr7X1kNh8cz+mFgx9v5jFdz1MfI84pq8C2zG+oAoeE5kFrUf7YkT5x4uLWBNg0DVZ5A==
|
integrity sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
workbox-routing "6.6.1"
|
workbox-routing "7.0.0"
|
||||||
workbox-strategies "6.6.1"
|
workbox-strategies "7.0.0"
|
||||||
|
|
||||||
workbox-range-requests@6.6.1:
|
workbox-range-requests@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39"
|
resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-7.0.0.tgz#97511901e043df27c1aa422adcc999a7751f52ed"
|
||||||
integrity sha512-4BDzk28govqzg2ZpX0IFkthdRmCKgAKreontYRC5YsAPB2jDtPNxqx3WtTXgHw1NZalXpcH/E4LqUa9+2xbv1g==
|
integrity sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-recipes@6.6.1:
|
workbox-recipes@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae"
|
resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-7.0.0.tgz#1a6a01c8c2dfe5a41eef0fed3fe517e8a45c6514"
|
||||||
integrity sha512-/oy8vCSzromXokDA+X+VgpeZJvtuf8SkQ8KL0xmRivMgJZrjwM3c2tpKTJn6PZA6TsbxGs3Sc7KwMoZVamcV2g==
|
integrity sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-cacheable-response "6.6.1"
|
workbox-cacheable-response "7.0.0"
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
workbox-expiration "6.6.1"
|
workbox-expiration "7.0.0"
|
||||||
workbox-precaching "6.6.1"
|
workbox-precaching "7.0.0"
|
||||||
workbox-routing "6.6.1"
|
workbox-routing "7.0.0"
|
||||||
workbox-strategies "6.6.1"
|
workbox-strategies "7.0.0"
|
||||||
|
|
||||||
workbox-routing@6.6.1, workbox-routing@^6.6.0:
|
workbox-routing@7.0.0, workbox-routing@^7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581"
|
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.0.0.tgz#6668438a06554f60645aedc77244a4fe3a91e302"
|
||||||
integrity sha512-j4ohlQvfpVdoR8vDYxTY9rA9VvxTHogkIDwGdJ+rb2VRZQ5vt1CWwUUZBeD/WGFAni12jD1HlMXvJ8JS7aBWTg==
|
integrity sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-strategies@6.6.1, workbox-strategies@^6.6.0:
|
workbox-strategies@7.0.0, workbox-strategies@^7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf"
|
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.0.0.tgz#dcba32b3f3074476019049cc490fe1a60ea73382"
|
||||||
integrity sha512-WQLXkRnsk4L81fVPkkgon1rZNxnpdO5LsO+ws7tYBC6QQQFJVI6v98klrJEjFtZwzw/mB/HT5yVp7CcX0O+mrw==
|
integrity sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
workbox-streams@6.6.1:
|
workbox-streams@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26"
|
resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-7.0.0.tgz#36722aecd04785f88b6f709e541c094fc658c0f9"
|
||||||
integrity sha512-maKG65FUq9e4BLotSKWSTzeF0sgctQdYyTMq529piEN24Dlu9b6WhrAfRpHdCncRS89Zi2QVpW5V33NX8PgH3Q==
|
integrity sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
workbox-routing "6.6.1"
|
workbox-routing "7.0.0"
|
||||||
|
|
||||||
workbox-sw@6.6.1:
|
workbox-sw@7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c"
|
resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-7.0.0.tgz#7350126411e3de1409f7ec243df8d06bb5b08b86"
|
||||||
integrity sha512-R7whwjvU2abHH/lR6kQTTXLHDFU2izht9kJOvBRYK65FbwutT4VvnUAJIgHvfWZ/fokrOPhfoWYoPCMpSgUKHQ==
|
integrity sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==
|
||||||
|
|
||||||
workbox-webpack-plugin@^6.6.0:
|
workbox-webpack-plugin@^7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531"
|
resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-7.0.0.tgz#6c61661a2cacde1239192a5877a041a2943d1a55"
|
||||||
integrity sha512-zpZ+ExFj9NmiI66cFEApyjk7hGsfJ1YMOaLXGXBoZf0v7Iu6hL0ZBe+83mnDq3YYWAfA3fnyFejritjOHkFcrA==
|
integrity sha512-R1ZzCHPfzeJjLK2/TpKUhxSQ3fFDCxlWxgRhhSjMQLz3G2MlBnyw/XeYb34e7SGgSv0qG22zEhMIzjMNqNeKbw==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-json-stable-stringify "^2.1.0"
|
fast-json-stable-stringify "^2.1.0"
|
||||||
pretty-bytes "^5.4.1"
|
pretty-bytes "^5.4.1"
|
||||||
upath "^1.2.0"
|
upath "^1.2.0"
|
||||||
webpack-sources "^1.4.3"
|
webpack-sources "^1.4.3"
|
||||||
workbox-build "6.6.1"
|
workbox-build "7.0.0"
|
||||||
|
|
||||||
workbox-window@6.6.1, workbox-window@^6.6.0:
|
workbox-window@7.0.0, workbox-window@^7.0.0:
|
||||||
version "6.6.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e"
|
resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-7.0.0.tgz#a683ab33c896e4f16786794eac7978fc98a25d08"
|
||||||
integrity sha512-wil4nwOY58nTdCvif/KEZjQ2NP8uk3gGeRNy2jPBbzypU4BT4D9L8xiwbmDBpZlSgJd2xsT9FvSNU0gsxV51JQ==
|
integrity sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/trusted-types" "^2.0.2"
|
"@types/trusted-types" "^2.0.2"
|
||||||
workbox-core "6.6.1"
|
workbox-core "7.0.0"
|
||||||
|
|
||||||
wrap-ansi@^5.1.0:
|
wrap-ansi@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
|
@ -12532,9 +12556,9 @@ write-file-atomic@^5.0.1:
|
||||||
signal-exit "^4.0.1"
|
signal-exit "^4.0.1"
|
||||||
|
|
||||||
ws@^6.2.1:
|
ws@^6.2.1:
|
||||||
version "6.2.1"
|
version "6.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
|
||||||
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
|
integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue