Compare commits

...

30 Commits

Author SHA1 Message Date
Eugen Rochko
8963f8c3c2 Bump version to 1.4.1 2017-05-28 19:30:38 +02:00
Nolan Lawson
5e41c26203 Use immutable list in UploadButton to avoid wasteful re-render (#3394) 2017-05-28 19:15:35 +02:00
Clworld
45837c533e Re-add stream end log for WebSocket (#3397) 2017-05-28 19:14:44 +02:00
Eugen Rochko
3fa8512474 Fix video having black border on top due to regression from #2608 (#3392)
The combination of object-fit, relative position 50% from top and translating it
back upwards 50% is what allows us to crop the video properly, so it needs to
be +50%-50%
2017-05-28 19:11:47 +02:00
ster
0e20de9f89 Ukrainian translation: fix typo (#3393) 2017-05-28 19:11:29 +02:00
Nolan Lawson
24d645b7d0 Fix IntersectionObserver isIntersecting in Edge (#3365) 2017-05-28 16:45:42 +02:00
Eugen Rochko
7b23f79d41 Bump version to 1.4.0.6 2017-05-28 16:32:53 +02:00
Eugen Rochko
3b4095cf1b Update bootsnap to 0.3.0 (fix xattr.h error) (#3390) 2017-05-28 16:32:29 +02:00
Nolan Lawson
28cbfb9f10 Simplify isIntersecting in status_list.js (#3371) 2017-05-28 16:26:35 +02:00
Akihiko Odaki
189a06d2a2 Fix Webpack Bundle Analyzer output for Webpacker (#3374)
Webpacker failed to parse output of Webpack when a module requires
non-existent module or has similar errors. This commit fixes the bug.
2017-05-28 16:26:16 +02:00
vidarlee
450441fc11 i18n: Add some tags for Chinese translation (#3379)
* Enhance for the Chinese translation

* Add filtered_languages for Chinese Translation include CN/HK/TW for PR #3175

* i18n: Add some tags for Chinese translation

* i18n: Add some tags for Chinese translation
2017-05-28 16:25:54 +02:00
Masoud Abkenar
b619362a36 Persian translation fixes and updates (#3380)
* Persian translation fix

* Persian translation of new strings
2017-05-28 16:25:45 +02:00
Eugen Rochko
425d02287a Improve streaming API cluster logging (#3370)
* Improve streaming API cluster logging

* Less verbose error middleware logging (stack trace useless there)

* Fix error logging

* Prevent potential issue

* Add missing "done()" in catch of Promise.all, websocket heartbeat re-implemented like in example

* I actually forgot a done(), the absolute madman
2017-05-28 16:25:26 +02:00
Clworld
2e429c0c25 Reject revoked access_token on Streaming API. (#3367) 2017-05-27 23:27:54 +02:00
Eugen Rochko
e0e12b0fee Bump version to 1.4.0.5 2017-05-27 16:56:47 +02:00
Eugen Rochko
62ca37884a Fix #2922 - Load stylesheet from "custom.css" entrypoint when present (#3332)
* Fix #2922 - Load stylesheet from "custom.css" entrypoint when present

This is pretty much the same way it worked as before, albeit with
having to create app/javascript/packs/custom.js with
require('../styles/custom.scss') (or whatever you want really), which
will be a blank slate for you to import whatever you want

* Remove old assets directory

* Extract font-awesome into common.css and always load it
2017-05-27 16:55:09 +02:00
jeroenpraat
f9180823bc Update Dutch strings for 1.4 (#3363)
* Update nl strings for 1.4

* Update nl strings for 1.4

* Update nl strings for 1.4

* nl strings (+1)

More new OTP strings will be translated another time
2017-05-27 16:28:32 +02:00
Quent-in
4b0c667c09 i18n update for Occitan language (#3362)
* Update simple_form.oc.yml

* Added check spam folder

* Update oc.json

abonats => seguidors

* Update oc.yml

* Update oc.json

Added translations + corrections

* Update confirmation_instructions.oc.html.erb

* Update confirmation_instructions.oc.text.erb

* i18n mailer

* Update reset_password_instructions.oc.html.erb
2017-05-27 15:50:40 +02:00
m4sk1n
1b732cad61 i18n: pl: pluralized (#3344)
Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
2017-05-27 13:04:42 +02:00
unarist
ecef03bb15 Fix empty flash message on the settings page (#3345) 2017-05-27 13:04:28 +02:00
happycoloredbanana
9642601126 Avoid comparing domains when looking for an exact match of a local account (#3336) 2017-05-27 00:55:08 +02:00
Eugen Rochko
3836d293a1 Add missing background center on public profile headers (#3340) 2017-05-27 00:54:00 +02:00
Eugen Rochko
0734e1fe33 Language filtering in streaming API (#3339) 2017-05-27 00:53:48 +02:00
Eugen Rochko
44cb08297c Fix some nil errors (#3338)
* Fix nil input not handled well in AuthorExtractor concern

* Fix hard error in ProcessFeedService when replied-to status has been deleted

* Fix nil errors in ProcessInteractionService when favourited status
cannot be found
2017-05-27 00:53:38 +02:00
Eugen Rochko
bd21afb5ed Replace onboarding elephant with friendlier graphic, shorter animation (#3337)
on status fade-in, fix buttons not having pointer cursor
2017-05-27 00:53:25 +02:00
m4sk1n
ef80ad17b3 Updated Polish translation (#3335)
* i18n: updated Polish translation

Signed-off-by: Marcin Mikołajczak <m4sk1n@vivaldi.net>

* i18n: completed Polish translation

Signed-off-by: Marcin Mikołajczak <m4sk1n@vivaldi.net>

* i18n: corrected Polish translation

Signed-off-by: Marcin Mikołajczak <m4sk1n@vivaldi.net>

* i18n: Updated Polish translation

Signed-off-by: Marcin Mikołajczak <me@m4sk.in>

* Update simple_form.pl.yml

* Update simple_form.pl.yml

* updated Polish translation

Signed-off-by: Marcin Mikołajczak <me@m4sk.in>

* Update pl.yml
2017-05-26 19:59:46 +02:00
Eugen Rochko
9ea4f37e78 Add "meta" attribute to return of POST /api/v1/media method as well (#3333) 2017-05-26 18:22:30 +02:00
Eugen Rochko
c48772fd3f Introduce react-textarea-autosize instead of using style.height side effects (#3334) 2017-05-26 18:22:23 +02:00
Akihiko Odaki
860e257a68 Remove redundant call of recent scope in AccountsController (#3330)
recent is included in paginate_by_max_id.
2017-05-26 16:35:25 +02:00
unarist
902d9e34b4 Remove status context construction in the React side (#3331)
because it may causes flicker on the conversation when it contains blocked/muted user's status.

We use `/api/v1/statuses/{id}/context` to obtain status ids in the
conversation which filters blocked/muted user, but also uses internal
cache constructed from `in_reply_to_id` by `normalizeStatus()` in
`reducers/timelines.js` on each status loading which doesn't filter.

So statuses appears in conversation if those are cached, even those
statuses are from blocked/muted user. Then context cache will be updated
with the result of the context API and those statuses will be removed.

I have left the `normalizeStatus()` function itself which is called many
functions in the file as a placeholder for now, but maybe it should be
removed completely.
2017-05-26 16:34:08 +02:00
48 changed files with 358 additions and 220 deletions

View File

@@ -19,7 +19,7 @@ gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder', '~> 0.6'
gem 'addressable', '~> 2.5'
gem 'bootsnap'
gem 'bootsnap', '~> 0.3'
gem 'cld3', '~> 3.1'
gem 'devise', '~> 4.2'
gem 'devise-two-factor', '~> 3.0'

View File

@@ -67,7 +67,7 @@ GEM
rack (>= 0.9.0)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootsnap (0.2.14)
bootsnap (0.3.0)
msgpack (~> 1.0)
brakeman (3.6.1)
builder (3.2.3)
@@ -474,7 +474,7 @@ DEPENDENCIES
aws-sdk (~> 2.9)
better_errors (~> 2.1)
binding_of_caller (~> 0.7)
bootsnap
bootsnap (~> 0.3)
brakeman (~> 3.6)
bullet (~> 5.5)
bundler-audit (~> 0.5)

View File

@@ -6,12 +6,12 @@ class AccountsController < ApplicationController
def show
respond_to do |format|
format.html do
@statuses = @account.statuses.permitted_for(@account, current_account).recent.paginate_by_max_id(20, params[:max_id], params[:since_id])
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
@statuses = cache_collection(@statuses, Status)
end
format.atom do
@entries = @account.stream_entries.recent.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
end

View File

@@ -12,13 +12,13 @@ class Auth::SessionsController < Devise::SessionsController
def create
super do |resource|
remember_me(resource)
flash[:notice] = nil
flash.delete(:notice)
end
end
def destroy
super
flash[:notice] = nil
flash.delete(:notice)
end
protected

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
module StyleHelper
def stylesheet_for_layout
if asset_exist? 'custom.css'
'custom'
else
'application'
end
end
def asset_exist?(path)
true if Webpacker::Manifest.lookup(path)
rescue Webpacker::FileLoader::NotFoundError
false
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { isRtl } from '../rtl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
const textAtCursorMatchesToken = (str, caretPosition) => {
let word;
@@ -69,10 +70,6 @@ class AutosuggestTextarea extends ImmutablePureComponent {
this.props.onSuggestionsClearRequested();
}
// auto-resize textarea
e.target.style.height = 'auto';
e.target.style.height = `${e.target.scrollHeight}px`;
this.props.onChange(e);
}
@@ -160,10 +157,6 @@ class AutosuggestTextarea extends ImmutablePureComponent {
}
}
reset () {
this.textarea.style.height = 'auto';
}
render () {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props;
const { suggestionsHidden, selectedSuggestion } = this.state;
@@ -175,8 +168,8 @@ class AutosuggestTextarea extends ImmutablePureComponent {
return (
<div className='autosuggest-textarea'>
<textarea
ref={this.setTextarea}
<Textarea
inputRef={this.setTextarea}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}

View File

@@ -27,7 +27,8 @@ class StatusList extends ImmutablePureComponent {
};
state = {
isIntersecting: [{ }],
isIntersecting: {},
intersectionCount: 0,
}
statusRefQueue = []
@@ -65,15 +66,33 @@ class StatusList extends ImmutablePureComponent {
attachIntersectionObserver () {
const onIntersection = (entries) => {
this.setState(state => {
const isIntersecting = { };
entries.forEach(entry => {
const statusId = entry.target.getAttribute('data-id');
state.isIntersecting[0][statusId] = entry.isIntersecting;
// Edge 15 doesn't support isIntersecting, but we can infer it from intersectionRatio
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12156111/
state.isIntersecting[statusId] = entry.intersectionRatio > 0;
});
return { isIntersecting: [state.isIntersecting[0]] };
// isIntersecting is a map of DOM data-id's to booleans (true for
// intersecting, false for non-intersecting).
//
// We always want to return true in shouldComponentUpdate() if
// this object changes, because onIntersection() is only called if
// something has changed.
//
// Now, we *could* use an immutable map or some other structure to
// diff the full map, but that would be pointless because the browser
// has already informed us that something has changed. So we can just
// use a regular object, which will be diffed by ImmutablePureComponent
// based on reference equality (i.e. it's always "unchanged") and
// then we just increment intersectionCount to force a change.
return {
isIntersecting: state.isIntersecting,
intersectionCount: state.intersectionCount + 1,
};
});
};
@@ -122,7 +141,7 @@ class StatusList extends ImmutablePureComponent {
render () {
const { statusIds, onScrollToBottom, scrollKey, shouldUpdateScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props;
const isIntersecting = this.state.isIntersecting[0];
const { isIntersecting } = this.state;
let loadMore = null;
let scrollableArea = null;

View File

@@ -67,7 +67,6 @@ class ComposeForm extends ImmutablePureComponent {
}
handleSubmit = () => {
this.autosuggestTextarea.reset();
this.props.onSubmit();
}

View File

@@ -3,6 +3,8 @@ import IconButton from '../../../components/icon_button';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
const messages = defineMessages({
upload: { id: 'upload_button.label', defaultMessage: 'Add media' },
@@ -10,7 +12,7 @@ const messages = defineMessages({
const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']).toArray(),
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
});
return mapStateToProps;
@@ -21,14 +23,14 @@ const iconStyle = {
lineHeight: '27px',
};
class UploadButton extends React.PureComponent {
class UploadButton extends ImmutablePureComponent {
static propTypes = {
disabled: PropTypes.bool,
onSelectFile: PropTypes.func.isRequired,
style: PropTypes.object,
resetFileKey: PropTypes.number,
acceptContentTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
intl: PropTypes.object.isRequired,
};
@@ -58,7 +60,7 @@ class UploadButton extends React.PureComponent {
ref={this.setRef}
type='file'
multiple={false}
accept={ acceptContentTypes.join(',')}
accept={ acceptContentTypes.toArray().join(',')}
onChange={this.handleChange}
disabled={disabled}
style={{ display: 'none' }}

View File

@@ -7,7 +7,7 @@
"account.followers": "پیگیران",
"account.follows": "پی می‌گیرد",
"account.follows_you": "پیگیر شماست",
"account.media": "Media",
"account.media": "رسانه",
"account.mention": "نام‌بردن از @{name}",
"account.mute": "بی‌صدا کردن @{name}",
"account.posts": "نوشته‌ها",
@@ -92,10 +92,10 @@
"navigation_bar.mutes": "کاربران بی‌صداشده",
"navigation_bar.preferences": "ترجیحات",
"navigation_bar.public_timeline": "نوشته‌های همه‌جا",
"notification.favourite": "{name} نوشتهٔ شما را پسندید",
"notification.follow": "{name} پیگیر شما شد",
"notification.mention": "{name} از شما نام برد",
"notification.reblog": "{name} نوشتهٔ شما را بازبوقید",
"notification.favourite": "{name} نوشتهٔ شما را پسندید",
"notification.follow": "{name} پیگیر شما شد",
"notification.mention": "{name} از شما نام برد",
"notification.reblog": "{name} نوشتهٔ شما را بازبوقید",
"notifications.clear": "پاک‌کردن اعلان‌ها",
"notifications.clear_confirmation": "واقعاً می‌خواهید همهٔ اعلان‌هایتان را برای همیشه پاک کنید؟",
"notifications.column_settings.alert": "اعلان در کامپیوتر",
@@ -123,7 +123,7 @@
"onboarding.page_six.read_guidelines": "لطفاً {guidelines} {domain} را بخوانید!",
"onboarding.page_six.various_app": "اپ‌های موبایل",
"onboarding.page_three.profile": "با ویرایش نمایه می‌توانید تصویر نمایه، نوشتهٔ معرفی، و نام نمایشی خود را تغییر دهید. ترجیحات دیگر شما هم آن‌جاست.",
"onboarding.page_three.search": "در نوار جستجو می‌توانید کاربران دیگر را بیابید یا هشتگ‌ها را ببینید، مانند {نقاشی} یا {معرفی}. برای یافتن افرادی که روی سرورهای دیگر هستند، شناسهٔ کامل آن‌ها را بنویسید.",
"onboarding.page_three.search": "در نوار جستجو می‌توانید کاربران دیگر را بیابید یا هشتگ‌ها را ببینید، مانند {illustration} یا {introductions}. برای یافتن افرادی که روی سرورهای دیگر هستند، شناسهٔ کامل آن‌ها را بنویسید.",
"onboarding.page_two.compose": "در ستون «نوشتن» می‌توانید نوشته‌های تازه بنویسید. همچنین با دکمه‌های زیرش می‌توانید تصویر اضافه کنید، حریم خصوصی نوشته را تنظیم کنید، و هشدار محتوا بگذارید.",
"onboarding.skip": "رد کن",
"privacy.change": "تنظیم حریم خصوصی نوشته‌ها",
@@ -151,7 +151,7 @@
"status.mute_conversation": "بی‌صداکردن گفتگو",
"status.open": "این نوشته را باز کن",
"status.reblog": "بازبوقیدن",
"status.reblogged_by": "{name} بازبوقید",
"status.reblogged_by": "{name} بازبوقید",
"status.reply": "پاسخ",
"status.replyAll": "به نوشته پاسخ دهید",
"status.report": "گزارش دادن @{name}",

View File

@@ -1,6 +1,6 @@
{
"account.block": "Blokkeer @{name}",
"account.block_domain": "Hide everything from {domain}",
"account.block_domain": "Negeer alles van {domain}",
"account.disclaimer": "Deze gebruiker zit op een andere server. Dit getal kan hoger zijn.",
"account.edit_profile": "Profiel bewerken",
"account.follow": "Volgen",
@@ -14,9 +14,9 @@
"account.report": "Rapporteer @{name}",
"account.requested": "Wacht op goedkeuring",
"account.unblock": "Deblokkeer @{name}",
"account.unblock_domain": "Unhide {domain}",
"account.unblock_domain": "{domain} niet meer negeren",
"account.unfollow": "Ontvolgen",
"account.unmute": "Negeer @{name} niet meer",
"account.unmute": "@{name} niet meer negeren",
"boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
"column.blocks": "Geblokkeerde gebruikers",
"column.community": "Lokale tijdlijn",
@@ -43,8 +43,8 @@
"confirmations.block.message": "Weet je zeker dat je {name} wilt blokkeren?",
"confirmations.delete.confirm": "Verwijderen",
"confirmations.delete.message": "Weet je zeker dat je deze toot wilt verwijderen?",
"confirmations.domain_block.confirm": "Hide entire domain",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
"confirmations.domain_block.confirm": "Negeer alles van deze server",
"confirmations.domain_block.message": "Weet je het echt, echt zeker dat je alles van {domain} wil negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en gewenst.",
"confirmations.mute.confirm": "Negeren",
"confirmations.mute.message": "Weet je zeker dat je {name} wilt negeren?",
"emoji_button.activity": "Activiteiten",
@@ -148,7 +148,7 @@
"status.load_more": "Meer laden",
"status.media_hidden": "Media verborgen",
"status.mention": "Vermeld @{name}",
"status.mute_conversation": "Mute conversation",
"status.mute_conversation": "Negeer conversatie",
"status.open": "Toot volledig tonen",
"status.reblog": "Boost",
"status.reblogged_by": "{name} boostte",
@@ -159,7 +159,7 @@
"status.sensitive_warning": "Gevoelige inhoud",
"status.show_less": "Minder tonen",
"status.show_more": "Meer tonen",
"status.unmute_conversation": "Unmute conversation",
"status.unmute_conversation": "Conversatie niet meer negeren",
"tabs_bar.compose": "Schrijven",
"tabs_bar.federated_timeline": "Globaal",
"tabs_bar.home": "Jouw tijdlijn",

View File

@@ -1,25 +1,25 @@
{
"account.block": "Blocar",
"account.block_domain": "Hide everything from {domain}",
"account.block": "Blocar @{name}",
"account.block_domain": "Tot amagar del domeni {domain}",
"account.disclaimer": "Aqueste compte es sus una autra instància. Los nombres pòdon èsser mai grandes.",
"account.edit_profile": "Modificar lo perfil",
"account.follow": "Sègre",
"account.followers": "Abonats",
"account.followers": "Seguidors",
"account.follows": "Abonaments",
"account.follows_you": "Vos sèc",
"account.media": "Media",
"account.mention": "Mencionar",
"account.mute": "Rescondre",
"account.media": "Mèdias",
"account.mention": "Mencionar @{name}",
"account.mute": "Rescondre @{name}",
"account.posts": "Estatuts",
"account.report": "Senhalar",
"account.report": "Senhalar @{name}",
"account.requested": "Invitacion mandada",
"account.unblock": "Desblocar",
"account.unblock_domain": "Unhide {domain}",
"account.unblock": "Desblocar @{name}",
"account.unblock_domain": "Desblocar {domain}",
"account.unfollow": "Quitar de sègre",
"account.unmute": "Quitar de rescondre",
"boost_modal.combo": "Podètz butar {combo} per passar aquò lo còp que ven",
"account.unmute": "Quitar de rescondre @{name}",
"boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven",
"column.blocks": "Personas blocadas",
"column.community": "Fil public local",
"column.community": "Flux dactualitat public local",
"column.favourites": "Favorits",
"column.follow_requests": "Demandas dabonament",
"column.home": "Acuèlh",
@@ -29,7 +29,7 @@
"column_back_button.label": "Tornar",
"column_subheading.navigation": "Navigacion",
"column_subheading.settings": "Paramètres",
"compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als abonats.",
"compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.",
"compose_form.lock_disclaimer.lock": "clavat",
"compose_form.placeholder": "A de qué pensatz ?",
"compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz daqueste{domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias a Mastodons. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas dindicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists",
@@ -40,13 +40,13 @@
"compose_form.spoiler_placeholder": "Avertiment",
"confirmation_modal.cancel": "Anullar",
"confirmations.block.confirm": "Blocar",
"confirmations.block.message": "Sètz segur de voler blocar {name}?",
"confirmations.block.message": "Sètz segur de voler blocar {name} ?",
"confirmations.delete.confirm": "Suprimir",
"confirmations.delete.message": "Sètz segur de voler suprimir lestatut ?",
"confirmations.domain_block.confirm": "Hide entire domain",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
"confirmations.domain_block.confirm": "Amagar tot lo domeni",
"confirmations.domain_block.message": "Sètz segur segur de voler blocar complètament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.",
"confirmations.mute.confirm": "Metre en silenci",
"confirmations.mute.message": "Sètz segur de voler metre en silenci {name}?",
"confirmations.mute.message": "Sètz segur de voler metre en silenci {name} ?",
"emoji_button.activity": "Activitat",
"emoji_button.flags": "Drapèus",
"emoji_button.food": "Manjar e beure",
@@ -57,13 +57,13 @@
"emoji_button.search": "Cercar...",
"emoji_button.symbols": "Simbòls",
"emoji_button.travel": "Viatges & lòcs",
"empty_column.community": "Lo fil public local es void. Escribètz quicòm per lo garnir !",
"empty_column.community": "Lo flux public local es void. Escribètz quicòm per lo garnir !",
"empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
"empty_column.home": "Pel moment segètz pas segun. Visitatz {public} o utilizatz la recèrca per vos connectar a dautras personas.",
"empty_column.home.inactivity": "Vòstra pagina dacuèlh es voida. Se sètz estat inactiu per un moment, serà tornada generar per vos dins una estona.",
"empty_column.home.public_timeline": "lo fil public",
"empty_column.home.public_timeline": "lo flux public",
"empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualquun per començar una conversacion.",
"empty_column.public": "I a pas res aquí ! Escribètz quicòm de public, o seguètz de personas dautras instàncias per garnir lo fil public.",
"empty_column.public": "I a pas res aquí ! Escribètz quicòm de public, o seguètz de personas dautras instàncias per garnir lo flux public.",
"follow_request.authorize": "Autorizar",
"follow_request.reject": "Regetar",
"getting_started.appsshort": "Apps",
@@ -83,16 +83,16 @@
"media_gallery.toggle_visible": "Modificar la visibilitat",
"missing_indicator.label": "Pas trobat",
"navigation_bar.blocks": "Personas blocadas",
"navigation_bar.community_timeline": "Fil public local",
"navigation_bar.community_timeline": "Flux public local",
"navigation_bar.edit_profile": "Modificar lo perfil",
"navigation_bar.favourites": "Favorits",
"navigation_bar.follow_requests": "Demandas d'abonament",
"navigation_bar.info": "Mai informacions",
"navigation_bar.logout": "Desconnexion",
"navigation_bar.mutes": "Muted users",
"navigation_bar.mutes": "Personas rescondudas",
"navigation_bar.preferences": "Preferéncias",
"navigation_bar.public_timeline": "Fil public global",
"notification.favourite": "{name} a apondut a sos favorits :",
"navigation_bar.public_timeline": "Flux public global",
"notification.favourite": "{name} a ajustat a sos favorits :",
"notification.follow": "{name} vos sèc.",
"notification.mention": "{name} vos a mencionat :",
"notification.reblog": "{name} a partejat vòstre estatut :",
@@ -100,14 +100,14 @@
"notifications.clear_confirmation": "Volètz vertadièrament levar totas vòstras las notificacions ?",
"notifications.column_settings.alert": "Notificacions localas",
"notifications.column_settings.favourite": "Favorits :",
"notifications.column_settings.follow": "Nòus abonats :",
"notifications.column_settings.follow": "Nòus seguidors :",
"notifications.column_settings.mention": "Mencions :",
"notifications.column_settings.reblog": "Partatges :",
"notifications.column_settings.show": "Mostrar dins la colomna",
"notifications.column_settings.sound": "Emetre un son",
"notifications.settings": "Paramètres de la colomna",
"onboarding.done": "Done",
"onboarding.next": "Next",
"onboarding.done": "Fach",
"onboarding.next": "Seguent",
"onboarding.page_five.public_timelines": "Lo flux local mòstra los estatuts publics del monde de vòstra intància, aquí {domain}. Lo flux federat mòstra los estatuts publics de tot lo mond sus {domain} sègon. Son los fluxes publics, un bon biais de trobar de mond.",
"onboarding.page_four.home": "Lo flux dacuèlh mòstra los estatuts del mond que seguètz.",
"onboarding.page_four.notifications": "La colomna de notificacions vos fa veire quand qualquun enteragís amb vos",
@@ -129,11 +129,11 @@
"privacy.change": "Ajustar la confidencialitat del messatge",
"privacy.direct.long": "Mostrar pas qua las personas mencionadas",
"privacy.direct.short": "Dirècte",
"privacy.private.long": "Mostrar pas qua vòstres abonats",
"privacy.private.long": "Mostrar pas qua vòstres seguidors",
"privacy.private.short": "Privat",
"privacy.public.long": "Mostrar dins los fils publics",
"privacy.public.long": "Mostrar dins los fluxes publics",
"privacy.public.short": "Public",
"privacy.unlisted.long": "Mostrar pas dins los fils publics",
"privacy.unlisted.long": "Mostrar pas dins los fluxes publics",
"privacy.unlisted.short": "Pas-listat",
"reply_indicator.cancel": "Anullar",
"report.heading": "Nòu senhalament",
@@ -153,10 +153,10 @@
"status.reblog": "Partejar",
"status.reblogged_by": "{name} a partejat :",
"status.reply": "Respondre",
"status.replyAll": "Reply to thread",
"status.replyAll": "Respondre a la conversacion",
"status.report": "Senhalar @{name}",
"status.sensitive_toggle": "Clicar per mostrar",
"status.sensitive_warning": "Contengut embarrassant",
"status.sensitive_warning": "Contengut sensible",
"status.show_less": "Tornar plegar",
"status.show_more": "Desplegar",
"status.unmute_conversation": "Conversacions amb silenci levat",
@@ -166,11 +166,11 @@
"tabs_bar.local_timeline": "Flux public local",
"tabs_bar.notifications": "Notificacions",
"upload_area.title": "Lisatz e depausatz per mandar",
"upload_button.label": "Apondre un mèdia",
"upload_button.label": "Ajustar un mèdia",
"upload_form.undo": "Anullar",
"upload_progress.label": "Mandadís…",
"video_player.expand": "Mostrar la vidèo",
"video_player.toggle_sound": "Activar/Desactivar lo son",
"video_player.toggle_visible": "Mostrar/Rescondre la vidèo",
"video_player.video_error": "Video could not be played"
"video_player.video_error": "Fracàs de la lectura de la vidèo"
}

View File

@@ -1,6 +1,6 @@
{
"account.block": "Blokuj @{name}",
"account.block_domain": "Hide everything from {domain}",
"account.block_domain": "Blokuj wszystko z {domain}",
"account.disclaimer": "Ten użytkownik pochodzi z innej instancji. Ta liczba może być większa.",
"account.edit_profile": "Edytuj profil",
"account.follow": "Obserwuj",
@@ -14,7 +14,7 @@
"account.report": "Zgłoś @{name}",
"account.requested": "Oczekująca prośba",
"account.unblock": "Odblokuj @{name}",
"account.unblock_domain": "Unhide {domain}",
"account.unblock_domain": "Odblokuj domenę {domain}",
"account.unfollow": "Przestań obserwować",
"account.unmute": "Cofnij wyciszenie @{name}",
"boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
@@ -43,8 +43,8 @@
"confirmations.block.message": "Czy na pewno chcesz zablokować {name}?",
"confirmations.delete.confirm": "Usuń",
"confirmations.delete.message": "Czy na pewno chcesz usunąć ten status?",
"confirmations.domain_block.confirm": "Hide entire domain",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
"confirmations.domain_block.confirm": "Ukryj wszysyko z domeny",
"confirmations.domain_block.message": "Czy na pewno chcesz zablokować całą domenę {domain}? Zwykle lepszym rozwiązaniem jest blokada lub wyciszenie kilku użytkowników.",
"confirmations.mute.confirm": "Wycisz",
"confirmations.mute.message": "Czy na pewno chcesz wyciszyć {name}?",
"emoji_button.activity": "Aktywność",
@@ -60,18 +60,18 @@
"empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby odbić piłeczkę!",
"empty_column.hashtag": "Nie ma postów oznaczonych tym hashtagiem. Możesz napisać pierwszy!",
"empty_column.home": "Nie obserwujesz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć ciekawych ludzi.",
"empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
"empty_column.home.inactivity": "Strumień jest pusty. Jeżeli nie było Cię tu ostatnio, zostanie on wypełniony wkrótce.",
"empty_column.home.public_timeline": "publiczna oś czasu",
"empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.",
"empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.",
"follow_request.authorize": "Autoryzuj",
"follow_request.reject": "Odrzuć",
"getting_started.appsshort": "Apps",
"getting_started.appsshort": "Aplikacje",
"getting_started.faq": "FAQ",
"getting_started.heading": "Dowiedz się",
"getting_started.open_source_notice": "Mastodon jest oprogramowaniem o otwartym źródle. Możesz pomóc w rozwoju lub zgłaszać błędy na GitHubie tutaj {github}.",
"getting_started.support": "{faq} • {userguide} • {apps}",
"getting_started.userguide": "User Guide",
"getting_started.userguide": "Podręcznik użytkownika",
"home.column_settings.advanced": "Zaawansowane",
"home.column_settings.basic": "Podstawowe",
"home.column_settings.filter_regex": "Filtruj z użyciem wyrażeń regularnych",
@@ -148,7 +148,7 @@
"status.load_more": "Załaduj więcej",
"status.media_hidden": "Zawartość multimedialna ukryta",
"status.mention": "Wspomnij o @{name}",
"status.mute_conversation": "Mute conversation",
"status.mute_conversation": "Wycisz konwersację",
"status.open": "Rozszerz ten status",
"status.reblog": "Podbij",
"status.reblogged_by": "{name} podbił",
@@ -159,7 +159,7 @@
"status.sensitive_warning": "Wrażliwa zawartość",
"status.show_less": "Pokaż mniej",
"status.show_more": "Pokaż więcej",
"status.unmute_conversation": "Unmute conversation",
"status.unmute_conversation": "Cofnij wyciezenie konwersacji",
"tabs_bar.compose": "Napisz",
"tabs_bar.federated_timeline": "Globalne",
"tabs_bar.home": "Strona główna",

View File

@@ -29,7 +29,7 @@
"column_back_button.label": "Назад",
"column_subheading.navigation": "Навігація",
"column_subheading.settings": "Налаштування",
"compose_form.lock_disclaimer": "Ваш акаунт не {locked}. Кожен може підписатися на Вас та бачити Ваші приватні посты.",
"compose_form.lock_disclaimer": "Ваш акаунт не {locked}. Кожен може підписатися на Вас та бачити Ваші приватні пости.",
"compose_form.lock_disclaimer.lock": "приватний",
"compose_form.placeholder": "Що у Вас на думці?",
"compose_form.privacy_disclaimer": "Ваш приватний допис буде доставлено до згаданих користувачів на доменах {domains}. Ви довіряєте {domainsCount, plural, one {цьому серверу} other {цим серверам}}? Приватність постів працює тільки на інстанціях Mastodon. Якщо {domains} {domainsCount, plural, one {не є інстанцією Mastodon} other {не є інстанціями Mastodon}}, приватність поста не буде активована, та він може бути передмухнутий або іншим чином показаний не позначеним Вами користувачам.",

View File

@@ -1,8 +1,5 @@
const perf = require('./performance');
// allow override variables here
require.context('../../assets/stylesheets/', false, /variables.*\.scss$/);
// import default stylesheet with variables
require('font-awesome/css/font-awesome.css');
require('../styles/application.scss');
@@ -23,9 +20,6 @@ function main() {
require.context('../images/', true);
// import customization styles
require.context('../../assets/stylesheets/', false, /custom.*\.scss$/);
onDomContentLoaded(() => {
const mountNode = document.getElementById('mastodon');
const props = JSON.parse(mountNode.getAttribute('data-props'));

View File

@@ -91,19 +91,6 @@ const initialState = Immutable.Map({
});
const normalizeStatus = (state, status) => {
const replyToId = status.get('in_reply_to_id');
const id = status.get('id');
if (replyToId) {
if (!state.getIn(['descendants', replyToId], Immutable.List()).includes(id)) {
state = state.updateIn(['descendants', replyToId], Immutable.List(), set => set.push(id));
}
if (!state.getIn(['ancestors', id], Immutable.List()).includes(replyToId)) {
state = state.updateIn(['ancestors', id], Immutable.List(), set => set.push(replyToId));
}
}
return state;
};

View File

@@ -1,6 +1,7 @@
.card {
background: $ui-base-color;
background-size: cover;
background-position: center;
padding: 60px 0;
padding-bottom: 0;
border-radius: 4px 4px 0 0;

View File

@@ -47,10 +47,11 @@ body {
button {
font-family: inherit;
}
cursor: pointer;
button:focus {
outline: none;
&:focus {
outline: none;
}
}
.app-holder {

View File

@@ -561,7 +561,7 @@
}
opacity: 1;
animation: fade 0.3s linear;
animation: fade 150ms linear;
&.status-direct {
background: lighten($ui-base-color, 8%);
@@ -3013,13 +3013,14 @@ button.icon-button.active i.fa-retweet {
.onboarding-modal__page-one {
display: flex;
align-items: center;
}
.onboarding-modal__page-one__elephant-friend {
background: url('../images/elephant-friend.png') no-repeat center center / contain;
width: 147px;
height: 160px;
margin-right: 10px;
background: url('../images/elephant-friend-1.png') no-repeat center center / contain;
width: 155px;
height: 193px;
margin-right: 15px;
}
@media screen and (max-width: 400px) {
@@ -3394,7 +3395,7 @@ button.icon-button.active i.fa-retweet {
object-fit: cover;
position: relative;
top: 50%;
transform: translateY(-35%);
transform: translateY(-50%);
width: 100%;
z-index: 1;
}

View File

@@ -59,7 +59,13 @@ class AccountSearchService < BaseService
end
def exact_match
@_exact_match ||= Account.find_remote(query_username, query_domain)
@_exact_match ||= begin
if domain_is_local?
Account.find_local(query_username)
else
Account.find_remote(query_username, query_domain)
end
end
end
def search_results

View File

@@ -2,6 +2,8 @@
module AuthorExtractor
def author_from_xml(xml)
return nil if xml.nil?
# Try <email> for acct
acct = xml.at_xpath('./xmlns:author/xmlns:email', xmlns: TagManager::XMLNS)&.content

View File

@@ -189,7 +189,7 @@ class ProcessFeedService < BaseService
def find_status(uri)
if TagManager.instance.local_id?(uri)
local_id = TagManager.instance.unique_tag_to_local_id(uri, 'Status')
return Status.find(local_id)
return Status.find_by(id: local_id)
end
Status.find_by(uri: uri)

View File

@@ -108,12 +108,18 @@ class ProcessInteractionService < BaseService
def favourite!(xml, from_account)
current_status = status(xml)
return if current_status.nil?
favourite = current_status.favourites.where(account: from_account).first_or_create!(account: from_account)
NotifyService.new.call(current_status.account, favourite)
end
def unfavourite!(xml, from_account)
current_status = status(xml)
return if current_status.nil?
favourite = current_status.favourites.where(account: from_account).first
favourite&.destroy
end

View File

@@ -1,5 +1,7 @@
object @media
attribute :id, :type
node(:url) { |media| full_asset_url(media.file.url(:original)) }
node(:url) { |media| full_asset_url(media.file.url(:original)) }
node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
node(:text_url) { |media| medium_url(media) }
node(:text_url) { |media| medium_url(media) }
node(:meta) { |media| media.file.meta }

View File

@@ -3,4 +3,4 @@ attributes :id, :remote_url, :type
node(:url) { |media| full_asset_url(media.file.url(:original)) }
node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
node(:text_url) { |media| media.local? ? medium_url(media) : nil }
node(:meta) { |media| media.file.meta }
node(:meta) { |media| media.file.meta }

View File

@@ -18,7 +18,8 @@
= ' - '
= title
= stylesheet_pack_tag 'application', media: 'all'
= stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag stylesheet_for_layout, media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
= csrf_meta_tags

View File

@@ -7,7 +7,7 @@
<p>Aprèp vòstra primièra connexion, poiretz accedir a la documentacion de laisina.</p>
<p>Pensatz tanben a gaitar a nòstras <%= link_to 'conditions d\'utilisation', terms_url %>.</p>
<p>Pensatz tanben a gaitar nòstras <%= link_to 'conditions d\'utilisation', terms_url %>.</p>
<p>Amistosament,</p>

View File

@@ -7,7 +7,7 @@ er confirmar vòstre inscripcion, mercés de clicar sul ligam seguent :
Aprèp vòstra primièra connexion, poiretz accedir a la documentacion de laisina.
Pensatz tanben a gaitar a nòstras <%= link_to 'conditions d\'utilisation', terms_url %>.
Pensatz tanben a gaitar nòstras <%= link_to 'conditions d\'utilisation', terms_url %>.
Amistosament,

View File

@@ -1,6 +1,6 @@
<p>Bonjorn <%= @resource.email %>&nbsp;!</p>
<p>Qualquun a demandat una reĩnicializacion de vòstre senhal per Mastodon. Podètz realizar la reĩnicializacion ne clicant sul ligam çai-jos.</p>
<p>Qualquun a demandat una reĩnicializacion de vòstre senhal per Mastodon. Podètz realizar la reĩnicializacion en clicant sul ligam çai-jos.</p>
<p><%= link_to 'Modificar mon senhal', edit_password_url(@resource, reset_password_token: @token) %></p>

View File

@@ -1,6 +1,6 @@
Bonjorn <%= @resource.email %>&nbsp;!
Qualquun a demandat una reĩnicializacion de vòstre senhal per Mastodon. Podètz realizar la reĩnicializacion ne clicant sul ligam çai-jos.</p>
Qualquun a demandat una reĩnicializacion de vòstre senhal per Mastodon. Podètz realizar la reĩnicializacion en clicant sul ligam çai-jos.</p>
<%= link_to 'Modificar mon senhal', edit_password_url(@resource, reset_password_token: @token) %>

View File

@@ -3,8 +3,8 @@ oc:
devise:
confirmations:
confirmed: Vòstra adreça de corrièl es ben estada validada.
send_instructions: Recebretz un corrièl per vos indicar çò que cal far per confirmar vòstra adreça de corrièl dins una estona.
send_paranoid_instructions: Se vòstra adreça existís dins nòstra basa de donadas, recebretz un corrièl per vos indicar çò que cal far per confirmar vòstra adreça de corrièl dins una estona.
send_instructions: Recebretz un corrièl per vos indicar çò que cal far per confirmar vòstra adreça de corrièl dins una estona. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
send_paranoid_instructions: Se vòstra adreça existís dins nòstra basa de donadas, recebretz un corrièl per vos indicar çò que cal far per confirmar vòstra adreça de corrièl dins una estona. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
failure:
already_authenticated: Sètz ja connectat.
inactive: Vòstre compte es pas encara activat.
@@ -29,8 +29,8 @@ oc:
success: Sètz ben autentificat dempuèi lo compte %{kind}.
passwords:
no_token: Podètz pas accedir a aquesta pagina sens venir dun corriel de reïnicializacion de senhal. Ses lo cas, mercés de verificar quavètz ben utilizat lURL donada de manièra complèta.
send_instructions: Recebretz un corrièl amb las instruccions per reĩnicializar vòstre senhal dins una estona.
send_paranoid_instructions: Se vòstra adreça de corrièl existís dins nòstra basa de donadas, recebretz un ligam per reĩnicializar vòstre senhal dins una estona.
send_instructions: Recebretz un corrièl amb las instruccions per reĩnicializar vòstre senhal dins una estona. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
send_paranoid_instructions: Se vòstra adreça de corrièl existís dins nòstra basa de donadas, recebretz un ligam per reĩnicializar vòstre senhal dins una estona. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
updated: Vòstre senhal es ben estat cambiat. Sètz ara connectat.
updated_not_active: Vòstre senhal es ben estat cambiat.
registrations:
@@ -38,16 +38,16 @@ oc:
signed_up: Benvengut ! Sètz ben marcat al malhum.
signed_up_but_inactive: Sètz ben marcat. Pasmens, avèm pas pogut vos connectar perque vòstre compte es pas encara validat.
signed_up_but_locked: Sètz ben marcat. Pasmens, avèm pas pogut vos connectar perque vòstre compte es pas encara blocat.
signed_up_but_unconfirmed: Un messatge amb un ligam de confirmacion es estat enviat a vòstra adreça de corrièl. Clicatz sul ligam per activar vòstre compte.
update_needs_confirmation: Avètz ben mes a jorn vòstre compte, mai nos cal verificar vòstra nòva adreça de corrièl. Mercés de verificar vòstres messatges e clicar sul ligam de confirmacion per confirmar vòstra nòva adreça de corrièl.
signed_up_but_unconfirmed: Un messatge amb un ligam de confirmacion es estat enviat a vòstra adreça de corrièl. Clicatz sul ligam per activar vòstre compte. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
update_needs_confirmation: Avètz ben mes a jorn vòstre compte, mai nos cal verificar vòstra nòva adreça de corrièl. Mercés de verificar vòstres messatges e clicar sul ligam de confirmacion per confirmar vòstra nòva adreça de corrièl. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
updated: Vòstre compte es estat mes a jorn amb succès.
sessions:
already_signed_out: Desconnectat amb succès.
signed_in: Connectat amb succès.
signed_out: Desconnectat amb succès.
unlocks:
send_instructions: Recebretz un corrièl amb las instruccions per o desblocar dins una estona.
send_paranoid_instructions: Se vòstre compte existís recebretz un corrièl amb las instruccions per o desblocar dins una estona.
send_instructions: Recebretz un corrièl amb las instruccions per o desblocar dins una estona. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
send_paranoid_instructions: Se vòstre compte existís recebretz un corrièl amb las instruccions per o desblocar dins una estona. Mercés de verificar tanben vòstre dorsièr de corrièls indesirables.
unlocked: Vòstre compte es estat desblocat amb succès. Mercés de vos connectar per contunhar.
errors:
messages:

View File

@@ -145,7 +145,7 @@ fa:
id: شناسه
mark_as_resolved: علامت‌گذاری به عنوان حل‌شده
report: 'گزارش #%{id}'
report_contents: Contents
report_contents: محتوا
reported_account: حساب گزارش‌شده
reported_by: گزارش از طرف
resolved: حل‌شده
@@ -322,7 +322,7 @@ fa:
sensitive_content: محتوای حساس
time:
formats:
default: "%b %d, %Y, %H:%M"
default: "%d %b %Y, %H:%M"
two_factor_authentication:
code_hint: برای تأیید، کدی را که برنامهٔ تأییدکننده ساخته است وارد کنید
description_html: اگر <strong>ورود دومرحله‌ای</strong> را فعال کنید، برای ورود به سیستم به تلفن خود نیاز خواهید داشت تا برایتان یک کد موقتی بسازد

View File

@@ -222,6 +222,7 @@ nl:
disable: Uitschakelen
enable: Inschakelen
instructions_html: "<strong>Scan deze QR-code in Google Authenticator of een soortgelijke app op jouw mobiele telefoon</strong>. Van nu af aan genereert deze app aanmeldcodes die je bij het aanmelden moet invoeren."
setup: Instellen
users:
invalid_email: E-mailadres is ongeldig
invalid_otp_token: Ongeldige tweestaps-aanmeldcode

View File

@@ -18,7 +18,7 @@ oc:
ethics: 'Ethical design: pas cap de reclama o traçador'
gifv: Partatge de GIFs e vidèos cortas
privacy: Nivèl de confidencialitat configurable per cada publicacion
public: Fluxes dactualitat publicsPublic timelines
public: Fluxes dactualitat publics
features_headline: Çò que fa que Mastodon es diferent
get_started: Venètz al malhum
links: Ligams
@@ -32,7 +32,7 @@ oc:
version: Version
accounts:
follow: Sègre
followers: Abonats
followers: Seguidors
following: Abonaments
nothing_here: I a pas res aquí !
people_followed_by: Lo mond que %{name} sèc
@@ -60,7 +60,7 @@ oc:
edit: Modificar
email: Corrièl
feed_url: Flux URL
followers: Abonats
followers: Seguidors
follows: Abonaments
ip: IP
location:
@@ -76,7 +76,7 @@ oc:
title: Moderacion
most_recent_activity: Activitat mai recenta
most_recent_ip: IP mai recenta
not_subscribed: Pas abonat
not_subscribed: Pas seguidor
order:
alphabetic: Alfabetic
most_recent: Mai recent
@@ -87,7 +87,7 @@ oc:
push_subscription_expires: Fin de labonament PuSH
reset: Reïnicializar
reset_password: Reïnicializar lo senhal
salmon_url: Salmon URL
salmon_url: URL Salmon
search: Cercar
show:
created_reports: Rapòrts creat per aqueste compte
@@ -109,7 +109,7 @@ oc:
create: Crear blocatge
hint: Lo blocatge empacharà pas la creacion de compte dins la basa de donadas, mai aplicarà la moderacion sus aquestes comptes.
severity:
desc_html: "<strong>Silenci</strong> farà venir invisibles los estatuts del compte al mond que son pas abonats. <strong>Suspendre</strong> levarà tot lo contengut del compte, los mèdias e las donadas de perfil."
desc_html: "<strong>Silenci</strong> farà venir invisibles los estatuts del compte al mond que son pas de seguidors. <strong>Suspendre</strong> levarà tot lo contengut del compte, los mèdias e las donadas de perfil."
silence: Silenci
suspend: Suspendre
title: Nòu blocatge domeni
@@ -160,6 +160,10 @@ oc:
title: Senhalament
unresolved: Pas resolguts
view: Veire
nsfw:
'true': Contengut sensible activat
'false': Sens contengut sensible
are_you_sure: Es segur ?
settings:
contact_information:
email: Picatz una adreça de corrièl
@@ -240,15 +244,15 @@ oc:
storage: Mèdias gardats
followers:
domain: Domeni
explanation_html: Se volètz vos assegurar de la confidencialitat de vòstres estatuts, vos cal saber qual es abonat a vòstre compte. <strong>Vòstres estatuts privats son enviats a totas las instàncias quan de mond que vos sègon.</strong>. Benlèu que volètz repassar vòstra lista e tirar los abonats savètz de dubtes tocant las politica de confidencialitat de lor instàncias.
followers_count: Nombre dabonats
explanation_html: Se volètz vos assegurar de la confidencialitat de vòstres estatuts, vos cal saber qual sèc vòstre compte. <strong>Vòstres estatuts privats son enviats a totas las instàncias quan de mond que vos sègon.</strong>. Benlèu que volètz repassar vòstra lista e tirar los seguidors savètz de dubtes tocant las politica de confidencialitat de lor instàncias.
followers_count: Nombre de seguidors
lock_link: Clavar vòstre compte
purge: Tirar dels abonats
purge: Tirar dels seguidors
success:
one: Soi a blocar los abonats dun domeni...
other: Soi a blocar los abonats de %{count} domenis...
one: Soi a blocar los seguidors dun domeni...
other: Soi a blocar los seguidors de %{count} domenis...
true_privacy_html: Mèfi que la <strong>vertadièra confidencialitat pòt solament èsser amb un chiframent del cap a la fin (end-to-end)</strong>.
unlocked_warning_html: Tot lo mond pòt vos sègre e veire sulpic vòstres estatuts privats. %{lock_link} per poder repassar e regetar los abonats.
unlocked_warning_html: Tot lo mond pòt vos sègre e veire sulpic vòstres estatuts privats. %{lock_link} per poder repassar e regetar los seguidors.
unlocked_warning_title: Vòstre compte es pas clavat
generic:
changes_saved_msg: Cambiaments ben realizats !
@@ -269,15 +273,15 @@ oc:
landing_strip_signup_html: Ses pas lo cas, podètz <a href="%{sign_up_path}">vos marcar aquí</a>.
media_attachments:
validations:
images_and_video: Se pòt ajustar una vidèo a un estatut que ten ja dimatges
images_and_video: Se pòt pas ajustar una vidèo a un estatut que ten ja dimatges
too_many: Se pòt pas ajustar mai de 4 fichièrs
notification_mailer:
digest:
body: 'Trobatz aquí un resumit de çò quavètz mancat dempuèi vòstra darrièra visita lo %{since}:'
mention: "%{name} vos a mencionat dins :"
new_followers_summary:
one: Avètz un nòu abonat ! Ouà !
other: Avètz %{count} nòus abonats ! Qué crane !
one: Avètz un nòu seguidor ! Ouà !
other: Avètz %{count} nòus seguidors ! Qué crane !
subject:
one: "Una nòva notificacion dempuèi vòstra darrièra visita \U0001F418"
other: "%{count} nòvas notificacions dempuèi vòstra darrièra visita \U0001F418"
@@ -310,7 +314,7 @@ oc:
back: Tornar a Mastodon
edit_profile: Modificar lo perfil
export: Export donadas
followers: Abonats autorizats
followers: Seguidors autorizats
import: Import
preferences: Preferéncias
settings: Paramètres
@@ -320,8 +324,8 @@ oc:
over_character_limit: limit de %{max} caractèrs passat
show_more: Ne veire mai
visibilities:
private: Abonats solament
private_long: Mostrar pas quals abonats
private: Seguidors solament
private_long: Mostrar pas quals seguidors
public: Public
public_long: Tot lo mond pòt veire
unlisted: Pas listat
@@ -332,7 +336,7 @@ oc:
sensitive_content: Contengut sensible
time:
formats:
default: "%b %d %Y a %H o %M"
default: "%b %d %Y a %Ho%M"
two_factor_authentication:
code_hint: Picatz lo còdi generat per vòstra aplicacion dautentificacion per confirmar
description_html: Sactivatz <strong> lautentificacion two-factor</strong>, vos caldrà vòstre mobil per vos connectar perque generarà un geton per vos daissar dintrar.

View File

@@ -160,6 +160,10 @@ pl:
title: Zgłoszenia
unresolved: Nierozwiązane
view: Wyświetl
nsfw:
'true': NSFW będzie wyświetlane
'false': NSFW nie będzie wyświetlane
are_you_sure: Czy na pewno?
settings:
contact_information:
email: Wprowadź publiczny adres e-mail
@@ -239,8 +243,8 @@ pl:
lock_link: Zablokuj swoje konto
purge: Usuń z obserwujących
success:
one: W procesie usuwania obserwujcych z jednej domeny…
other: W procesie usuwania obserwujących z %{count} domen…
one: W trakcie usuwania obserwujcych z jednej domeny…
other: W trakcie usuwania obserwujących z %{count} domen…
true_privacy_html: Pamiętaj, że <strong>rzeczywista prywatność może zostać uzyskana wyłącznie dzięki szyfrowaniu end-to-end</strong>.
unlocked_warning_html: Każdy może cię zaobserwować, aby natychmiastowo zobaczyć twoje statusy. %{lock_link} aby móc kontrolować obserwujących.
unlocked_warning_title: Twoje konto nie jest zablokowane
@@ -271,9 +275,13 @@ pl:
mention: "%{name} wspomniał o Tobie w:"
new_followers_summary:
one: Śledzi Cię nowa osoba! Gratulacje!
other: Kilka (%{count}) nowych osób Cię śledzi! Wspaniale!
few: (%{count}) nowe osoby śledzą Cię!
many: (%{count}) nowych osób Cię śledzi! Wspaniale!
other: (%{count}) nowych osób Cię śledzi! Wspaniale!
subject:
one: "1 nowe powiadomienie od Twojej ostatniej wizyty \U0001F418"
few: "%{count} nowe powiadomienia od Twojej ostatniej wizyty \U0001F418"
many: "%{count} nowych powiadomień od Twojej ostatniej wizyty \U0001F418"
other: "%{count} nowych powiadomień od Twojej ostatniej wizyty \U0001F418"
favourite:
body: 'Twój wpis został polubiony przez %{name}:'

View File

@@ -10,6 +10,10 @@ nl:
note: Maximaal 160 tekens
imports:
data: CSV-bestand dat op een andere Mastodon-server werd geëxporteerd
sessions:
otp: Voer de tweestaps-aanmeldcode vanaf jouw mobiele telefoon in of gebruik een van jouw herstelcode's.
user:
filtered_languages: De geselecteerde talen worden uit de lokale en globale tijdlijn verwijderd.
labels:
defaults:
avatar: Avatar
@@ -19,6 +23,7 @@ nl:
data: Gegevens
display_name: Weergavenaam
email: E-mailadres
filtered_languages: Talen filteren
header: Omslagfoto
locale: Taal
locked: Maak account besloten

View File

@@ -4,14 +4,20 @@ oc:
hints:
defaults:
avatar: PNG, GIF o JPG. Maximum 2 Mo. Serà retalhat en 120x120px
display_name: Maximum 30 caractèrs
header: PNG, GIF o JPG. Maximum 2 Mo. Serà retalhat en 700x335px
locked: Demanda quacceptatz manualament lo mond que vos sègon e botarà la visibilitat de vòstras publicacions coma accessiblas a vòstres abonats solament
note: Maximum 160 caractèrs
display_name:
one: 'Demòra encara <span class="name-counter">1</span> caractèr'
other: 'Demòran encara <span class="name-counter">%{count}</span> caractèrs'
header: PNG, GIF o JPG. Maximum 2 Mo. Serà retalhada en 700x335px
locked: Demanda quacceptetz manualament lo mond que vos sègon e botarà la visibilitat de vòstras publicacions coma accessiblas a vòstres seguidors solament
note:
one: 'Demòra encara <span class="name-counter">1</span> caractèr'
other: 'Demòran encara <span class="name-counter">%{count}</span> caractèrs'
imports:
data: Fichièr CSV exportat duna autra instància Mastodon
sessions:
otp: Picatz lo còdi dautentificacion en dos temps (Two factor code) de vòstre mobil o utilizatz un de vòstres còdis de recuperacion.
user:
filtered_languages: Las lengas seleccionadas seràn levadas de vòstre flux dactualitat
labels:
defaults:
avatar: Avatar

View File

@@ -6,11 +6,15 @@ pl:
avatar: PNG, GIF lub JPG. Maksymalnie 2MB. Zostanie zmniejszony do 120x120px
display_name:
one: 'Pozostał <span class="name-counter">1</span> znak.'
few: 'Pozostały <span class="name-counter">%{count}</span> znaki.'
many: 'Pozostało <span class="name-counter">%{count}</span> znaków'
other: 'Pozostało <span class="name-counter">%{count}</span> znaków'
header: PNG, GIF lub JPG. Maksymalnie 2MB. Zostanie zmniejszony do 700x335px
locked: Musisz akceptować obserwacje; Twoje wpisy są domyślnie widoczne tylko dla Twoich obserwujących
note:
one: 'Pozostał <span class="name-counter">1</span> znak.'
few: 'Pozostały <span class="name-counter">%{count}</span> znaki.'
many: 'Pozostało <span class="name-counter">%{count}</span> znaków'
other: 'Pozostało <span class="name-counter">%{count}</span> znaków'
imports:
data: Plik CSV wyeksportowany z innej instancji Mastodona

View File

@@ -54,6 +54,7 @@ zh-CN:
are_you_sure: 你确定吗?
confirm: 确认
confirmed: 已确认
disable_two_factor_authentication: 两步认证无效
display_name: 显示名称
domain: 域名
edit: 编辑
@@ -61,6 +62,7 @@ zh-CN:
feed_url: 订阅 URL
followers: 关注者
follows: 正在关注
ip: IP地址
location:
all: 全部
local: 本地
@@ -83,7 +85,10 @@ zh-CN:
profile_url: 个人文件 URL
public: 公共
push_subscription_expires: 推送订阅过期
reset: 重置
reset_password: 重置密码
salmon_url: Salmon 反馈 URL
search: 搜索
show:
created_reports: 这个账户创建的报告
report: 报告
@@ -116,6 +121,7 @@ zh-CN:
severity: 阻隔程度
show:
affected_accounts:
one: 数据库中有1个账户受影响
other: 数据库中有%{count}个账户受影响
retroactive:
silence: 对此域名的所有账户取消静音
@@ -153,6 +159,10 @@ zh-CN:
title: 举报
unresolved: 未处理
view: 查看
nsfw:
'true': NSFW有效
'false': NSFW无效
are_you_sure: 你确定吗?
settings:
contact_information:
email: 输入一个公开的电邮地址
@@ -218,6 +228,7 @@ zh-CN:
'422':
content: 无法确认登录信息。你是不是屏蔽了 Cookie
title: 无法确认登录信息
'429': 被限制
exports:
blocks: 被你封锁的用户
csv: CSV
@@ -239,6 +250,7 @@ zh-CN:
powered_by: 基于 %{link} 构建
save_changes: 保存
validation_errors:
one: 出错啦!请确认以下出错的地方,修改之后再来一次:
other: 出错啦!请确认以下 %{count} 处出错的地方,修改之后再来一次:
imports:
preface: 你可以在此导入你在其他服务器实例所导出的数据文件,包括︰你所关注、封锁的用户。
@@ -250,6 +262,10 @@ zh-CN:
upload: 上载
landing_strip_html: <strong>%{name}</strong> 是一个在 %{link_to_root_path} 的用户。只要你是象毛世界里Mastodon、GNU social任一服务器实例的用户便可以跨站关注此站用户并与其沟通。
landing_strip_signup_html: 如果你没有这类账户,欢迎在<a href="%{sign_up_path}">此处登记</a>。
media_attachments:
validations:
images_and_video: 无法添加视频到一个已经包含图片的嘟文中
too_many: 最多只能添加4张图片
notification_mailer:
digest:
body: 自从你在%{since}使用%{instance}以后,错过了这些嘟嘟滴滴:
@@ -318,10 +334,12 @@ zh-CN:
disable: 停用
enable: 启用
enabled_success: 已成功启用两步认证
generate_recovery_codes: 生成恢复代码
instructions_html: "<strong>请用你手机的认证器应用(如 Google Authenticator、Authy扫描这里的 QR 二维码</strong>。在两步认证启用后,你登录时将需要使用此应用程序产生的认证码。"
lost_recovery_codes: 如果你丢了手机,你可以用恢复代码重新访问你的账户。如果你丢了恢复代码,也可以在这里重新生成一个,不过以前的恢复代码就失效了。<del>(废话)</del>
manual_instructions: 如果你无法扫描 QR 二维码,请手动输入这个文本密码︰
recovery_codes_regenerated: 已成功重新生成恢复代码
recovery_instructions: 如果你的手机无法使用,你可以使用下面的任何恢复代码来恢复你的账号。请保管好你的恢复代码以防泄漏(例如你可以打印好它们并和重要文档一起保存)。
setup: 设置
wrong_code: 你输入的认证码并不正确!可能服务器时间和你手机不一致,请检查你手机的时钟,或与本站管理员联系。
users:

View File

@@ -34,6 +34,7 @@ module.exports = merge(sharedConfig, {
analyzerMode: 'static',
generateStatsFile: true,
openAnalyzer: false,
logLevel: 'silent', // do not bother Webpacker, who runs with --json and parses stdout
}),
],
});

View File

@@ -53,6 +53,12 @@ module.exports = {
// be loaded together
return false;
}
if (module.resource && /node_modules\/font-awesome/.test(module.resource)) {
// extract vendor css into common module
return true;
}
return count >= 2;
},
}),

View File

@@ -13,11 +13,11 @@ module Mastodon
end
def patch
0
1
end
def pre
4
nil
end
def to_a

View File

@@ -90,6 +90,7 @@
"react-router": "^2.8.0",
"react-router-scroll": "^0.3.2",
"react-simple-dropdown": "^1.1.4",
"react-textarea-autosize": "^5.0.6",
"react-toggle": "^2.1.1",
"redis": "^2.6.5",
"redux": "^3.6.0",
@@ -101,6 +102,7 @@
"sass-loader": "^6.0.3",
"stringz": "^0.1.2",
"style-loader": "^0.16.1",
"throng": "^4.0.0",
"uuid": "^3.0.1",
"uws": "^0.14.5",
"webpack": "^2.4.1",

View File

@@ -33,25 +33,25 @@ describe AccountSearchService do
describe 'searching local and remote users' do
describe "when only '@'" do
before do
allow(Account).to receive(:find_remote)
allow(Account).to receive(:find_local)
allow(Account).to receive(:search_for)
subject.call('@', 10)
end
it 'uses find_remote with empty query to look for local accounts' do
expect(Account).to have_received(:find_remote).with('', nil)
it 'uses find_local with empty query to look for local accounts' do
expect(Account).to have_received(:find_local).with('')
end
end
describe 'when no domain' do
before do
allow(Account).to receive(:find_remote)
allow(Account).to receive(:find_local)
allow(Account).to receive(:search_for)
subject.call('one', 10)
end
it 'uses find_remote with nil domain to look for local accounts' do
expect(Account).to have_received(:find_remote).with('one', nil)
it 'uses find_local to look for local accounts' do
expect(Account).to have_received(:find_local).with('one')
end
it 'uses search_for to find matches' do
@@ -101,6 +101,25 @@ describe AccountSearchService do
end
end
describe 'when there is a local domain' do
around do |example|
before = Rails.configuration.x.local_domain
example.run
Rails.configuration.x.local_domain = before
end
it 'returns exact match first' do
remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e')
remote_too = Fabricate(:account, username: 'b', domain: 'remote', display_name: 'e')
exact = Fabricate(:account, username: 'e')
Rails.configuration.x.local_domain = 'example.com'
results = subject.call('e@example.com', 2)
expect(results.size).to eq 2
expect(results).to eq([exact, remote]).or eq([exact, remote_too])
end
end
describe 'when there is a domain but no exact match' do
it 'follows the remote account when resolve is true' do
service = double(call: nil)

View File

@@ -1,5 +1,5 @@
import os from 'os';
import cluster from 'cluster';
import throng from 'throng';
import dotenv from 'dotenv';
import express from 'express';
import http from 'http';
@@ -16,6 +16,8 @@ dotenv.config({
path: env === 'production' ? '.env.production' : '.env',
});
log.level = process.env.LOG_LEVEL || 'verbose';
const dbUrlToConfig = (dbUrl) => {
if (!dbUrl) {
return {};
@@ -65,24 +67,15 @@ const redisUrlToClient = (defaultConfig, redisUrl) => {
}));
};
if (cluster.isMaster) {
// Cluster master
const core = +process.env.STREAMING_CLUSTER_NUM || (env === 'development' ? 1 : Math.max(os.cpus().length - 1, 1));
const numWorkers = +process.env.STREAMING_CLUSTER_NUM || (env === 'development' ? 1 : Math.max(os.cpus().length - 1, 1));
const fork = () => {
const worker = cluster.fork();
const startMaster = () => {
log.info(`Starting streaming API server master with ${numWorkers} workers`);
};
worker.on('exit', (code, signal) => {
log.error(`Worker died with exit code ${code}, signal ${signal} received.`);
setTimeout(() => fork(), 0);
});
};
const startWorker = (workerId) => {
log.info(`Starting worker ${workerId}`);
for (let i = 0; i < core; i++) fork();
log.info(`Starting streaming API server master with ${core} workers`);
} else {
// Cluster worker
const pgConfigs = {
development: {
database: 'mastodon_development',
@@ -130,6 +123,7 @@ if (cluster.isMaster) {
if (!callbacks) {
return;
}
callbacks.forEach(callback => callback(message));
});
@@ -168,7 +162,7 @@ if (cluster.isMaster) {
return;
}
client.query('SELECT oauth_access_tokens.resource_owner_id, users.account_id FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 LIMIT 1', [token], (err, result) => {
client.query('SELECT oauth_access_tokens.resource_owner_id, users.account_id, users.filtered_languages FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token], (err, result) => {
done();
if (err) {
@@ -185,6 +179,7 @@ if (cluster.isMaster) {
}
req.accountId = result.rows[0].account_id;
req.filteredLanguages = result.rows[0].filtered_languages;
next();
});
@@ -214,9 +209,9 @@ if (cluster.isMaster) {
};
const errorMiddleware = (err, req, res, next) => {
log.error(req.requestId, err);
log.error(req.requestId, err.toString());
res.writeHead(err.statusCode || 500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: err.statusCode ? `${err}` : 'An unexpected error occurred' }));
res.end(JSON.stringify({ error: err.statusCode ? err.toString() : 'An unexpected error occurred' }));
};
const placeholders = (arr, shift = 0) => arr.map((_, i) => `$${i + 1 + shift}`).join(', ');
@@ -248,6 +243,12 @@ if (cluster.isMaster) {
const targetAccountIds = [unpackedPayload.account.id].concat(unpackedPayload.mentions.map(item => item.id)).concat(unpackedPayload.reblog ? [unpackedPayload.reblog.account.id] : []);
const accountDomain = unpackedPayload.account.acct.split('@')[1];
if (Array.isArray(req.filteredLanguages) && req.filteredLanguages.includes(unpackedPayload.language)) {
log.silly(req.requestId, `Message ${unpackedPayload.id} filtered by language (${unpackedPayload.language})`);
done();
return;
}
const queries = [
client.query(`SELECT 1 FROM blocks WHERE account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 1)}) UNION SELECT 1 FROM mutes WHERE account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 1)})`, [req.accountId].concat(targetAccountIds)),
];
@@ -265,6 +266,7 @@ if (cluster.isMaster) {
transmit();
}).catch(err => {
done();
log.error(err);
});
});
@@ -303,35 +305,24 @@ if (cluster.isMaster) {
};
// Setup stream output to WebSockets
const streamToWs = (req, ws) => {
const heartbeat = setInterval(() => {
// TODO: Can't add multiple listeners, due to the limitation of uws.
if (ws.readyState !== ws.OPEN) {
log.verbose(req.requestId, `Ending stream for ${req.accountId}`);
clearInterval(heartbeat);
return;
}
const streamToWs = (req, ws) => (event, payload) => {
if (ws.readyState !== ws.OPEN) {
log.error(req.requestId, 'Tried writing to closed socket');
return;
}
ws.ping();
}, 15000);
return (event, payload) => {
if (ws.readyState !== ws.OPEN) {
log.error(req.requestId, 'Tried writing to closed socket');
return;
}
ws.send(JSON.stringify({ event, payload }));
};
ws.send(JSON.stringify({ event, payload }));
};
// Setup stream end for WebSockets
const streamWsEnd = ws => (id, listener) => {
const streamWsEnd = (req, ws) => (id, listener) => {
ws.on('close', () => {
log.verbose(req.requestId, `Ending stream for ${req.accountId}`);
unsubscribe(id, listener);
});
ws.on('error', e => {
log.verbose(req.requestId, `Ending stream for ${req.accountId}`);
unsubscribe(id, listener);
});
};
@@ -366,6 +357,12 @@ if (cluster.isMaster) {
const token = location.query.access_token;
const req = { requestId: uuid.v4() };
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
});
accountFromToken(token, req, err => {
if (err) {
log.error(req.requestId, err);
@@ -375,19 +372,19 @@ if (cluster.isMaster) {
switch(location.query.stream) {
case 'user':
streamFrom(`timeline:${req.accountId}`, req, streamToWs(req, ws), streamWsEnd(ws));
streamFrom(`timeline:${req.accountId}`, req, streamToWs(req, ws), streamWsEnd(req, ws));
break;
case 'public':
streamFrom('timeline:public', req, streamToWs(req, ws), streamWsEnd(ws), true);
streamFrom('timeline:public', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'public:local':
streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(ws), true);
streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'hashtag':
streamFrom(`timeline:hashtag:${location.query.tag}`, req, streamToWs(req, ws), streamWsEnd(ws), true);
streamFrom(`timeline:hashtag:${location.query.tag}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'hashtag:local':
streamFrom(`timeline:hashtag:${location.query.tag}:local`, req, streamToWs(req, ws), streamWsEnd(ws), true);
streamFrom(`timeline:hashtag:${location.query.tag}:local`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
default:
ws.close();
@@ -395,16 +392,40 @@ if (cluster.isMaster) {
});
});
const wsInterval = setInterval(() => {
wss.clients.forEach(ws => {
if (ws.isAlive === false) {
ws.terminate();
return;
}
ws.isAlive = false;
ws.ping('', false, true);
});
}, 30000);
server.listen(process.env.PORT || 4000, () => {
log.level = process.env.LOG_LEVEL || 'verbose';
log.info(`Starting streaming API server worker on ${server.address().address}:${server.address().port}`);
log.info(`Worker ${workerId} now listening on ${server.address().address}:${server.address().port}`);
});
process.on('SIGINT', exit);
process.on('SIGTERM', exit);
process.on('exit', exit);
function exit() {
const onExit = () => {
log.info(`Worker ${workerId} exiting, bye bye`);
server.close();
}
}
};
const onError = (err) => {
log.error(err);
};
process.on('SIGINT', onExit);
process.on('SIGTERM', onExit);
process.on('exit', onExit);
process.on('error', onError);
};
throng({
workers: numWorkers,
lifetime: Infinity,
start: startWorker,
master: startMaster,
});

View File

@@ -5590,6 +5590,12 @@ react-test-renderer@^15.5.4:
fbjs "^0.8.9"
object-assign "^4.1.0"
react-textarea-autosize@^5.0.6:
version "5.0.6"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-5.0.6.tgz#a3742e0a319484021b4dbfa1519df287768f2133"
dependencies:
prop-types "^15.5.8"
react-toggle@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-toggle/-/react-toggle-2.1.1.tgz#80600a64417a1acc8aaa4c1477f7fbdb88b988fb"
@@ -6492,6 +6498,12 @@ text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
throng@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/throng/-/throng-4.0.0.tgz#983c6ba1993b58eae859998aa687ffe88df84c17"
dependencies:
lodash.defaults "^4.0.1"
through@2, through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"