Updated from tootsuite

This commit is contained in:
Ondřej Hruška 2017-07-17 20:03:57 +02:00
commit c727eae441
34 changed files with 221 additions and 29 deletions

View File

@ -15,6 +15,7 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
&& apk -U upgrade \ && apk -U upgrade \
&& apk add -t build-dependencies \ && apk add -t build-dependencies \
build-base \ build-base \
libidn-dev \
libxml2-dev \ libxml2-dev \
libxslt-dev \ libxslt-dev \
postgresql-dev \ postgresql-dev \
@ -27,6 +28,7 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
git \ git \
icu-dev \ icu-dev \
imagemagick@edge \ imagemagick@edge \
libidn \
libpq \ libpq \
libxml2 \ libxml2 \
libxslt \ libxslt \

View File

@ -20,7 +20,6 @@ class ActivityPub::OutboxesController < Api::BaseController
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: account_outbox_url(@account), id: account_outbox_url(@account),
type: :ordered, type: :ordered,
current: account_outbox_url(@account),
size: @account.statuses_count, size: @account.statuses_count,
items: @statuses items: @statuses
) )

View File

@ -21,7 +21,6 @@ class FollowerAccountsController < ApplicationController
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: account_followers_url(@account), id: account_followers_url(@account),
type: :ordered, type: :ordered,
current: account_followers_url(@account),
size: @account.followers_count, size: @account.followers_count,
items: @follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) } items: @follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) }
) )

View File

@ -21,7 +21,6 @@ class FollowingAccountsController < ApplicationController
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: account_following_index_url(@account), id: account_following_index_url(@account),
type: :ordered, type: :ordered,
current: account_following_index_url(@account),
size: @account.following_count, size: @account.following_count,
items: @follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.target_account) } items: @follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.target_account) }
) )

View File

@ -23,7 +23,6 @@ class TagsController < ApplicationController
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: tag_url(@tag), id: tag_url(@tag),
type: :ordered, type: :ordered,
current: tag_url(@tag),
size: @tag.statuses.count, size: @tag.statuses.count,
items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) } items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) }
) )

View File

@ -32,7 +32,7 @@ export default class ExtendedVideoPlayer extends React.PureComponent {
render () { render () {
return ( return (
<div className='extended-video-player' style={{ width: this.props.width, height: this.props.height }}> <div className='extended-video-player'>
<video <video
ref={this.setRef} ref={this.setRef}
src={this.props.src} src={this.props.src}

View File

@ -140,7 +140,7 @@ export default class ComposeForm extends ImmutablePureComponent {
handleEmojiPick = (data) => { handleEmojiPick = (data) => {
const position = this.autosuggestTextarea.textarea.selectionStart; const position = this.autosuggestTextarea.textarea.selectionStart;
const emojiChar = String.fromCodePoint(parseInt(data.unicode, 16)); const emojiChar = data.unicode.split('-').map(code => String.fromCodePoint(parseInt(code, 16))).join('');
this._restoreCaret = position + emojiChar.length + 1; this._restoreCaret = position + emojiChar.length + 1;
this.props.onPickEmoji(position, data); this.props.onPickEmoji(position, data);
} }

View File

@ -8,8 +8,6 @@ function main() {
const React = require('react'); const React = require('react');
const ReactDOM = require('react-dom'); const ReactDOM = require('react-dom');
require.context('../images/', true);
if (window.history && history.replaceState) { if (window.history && history.replaceState) {
const { pathname, search, hash } = window.location; const { pathname, search, hash } = window.location;
const path = pathname + search + hash; const path = pathname + search + hash;

View File

@ -126,7 +126,7 @@ const insertSuggestion = (state, position, token, completion) => {
}; };
const insertEmoji = (state, position, emojiData) => { const insertEmoji = (state, position, emojiData) => {
const emoji = String.fromCodePoint(parseInt(emojiData.unicode, 16)); const emoji = emojiData.unicode.split('-').map(code => String.fromCodePoint(parseInt(code, 16))).join('');
return state.withMutations(map => { return state.withMutations(map => {
map.update('text', oldText => `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`); map.update('text', oldText => `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`);

View File

@ -4,4 +4,6 @@ import { start } from 'rails-ujs';
require('font-awesome/css/font-awesome.css'); require('font-awesome/css/font-awesome.css');
require('mastodon-application-style'); require('mastodon-application-style');
require.context('../images/', true);
start(); start();

View File

@ -7,8 +7,6 @@ import loadPolyfills from '../mastodon/load_polyfills';
import { processBio } from '../glitch/util/bio_metadata'; import { processBio } from '../glitch/util/bio_metadata';
import ready from '../mastodon/ready'; import ready from '../mastodon/ready';
require.context('../images/', true);
const { localeData } = getLocale(); const { localeData } = getLocale();
localeData.forEach(IntlRelativeFormat.__addLocaleData); localeData.forEach(IntlRelativeFormat.__addLocaleData);

View File

@ -1384,8 +1384,8 @@
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
width: 100%; max-width: 100%;
height: 100%; max-height: 100%;
background-image: none; background-image: none;
} }
@ -3313,8 +3313,9 @@ button.icon-button.active i.fa-retweet {
video { video {
max-width: 80vw; max-width: 80vw;
max-height: 80vh; max-height: 80vh;
width: 100%; width: auto;
height: auto; height: auto;
margin: auto;
} }
.extended-video-player, .extended-video-player,
@ -3330,6 +3331,10 @@ button.icon-button.active i.fa-retweet {
background: url('../images/void.png') repeat; background: url('../images/void.png') repeat;
object-fit: contain; object-fit: contain;
} }
.react-swipeable-view-container {
max-width: 80vw;
}
} }
.media-modal__close { .media-modal__close {

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::CollectionPresenter < ActiveModelSerializers::Model class ActivityPub::CollectionPresenter < ActiveModelSerializers::Model
attributes :id, :type, :current, :size, :items attributes :id, :type, :size, :items
end end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ActivityPub::AcceptFollowSerializer < ActiveModel::Serializer
attributes :type, :actor
has_one :object, serializer: ActivityPub::FollowSerializer
def type
'Accept'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.target_account)
end
end

View File

@ -7,6 +7,8 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
:inbox, :outbox, :preferred_username, :inbox, :outbox, :preferred_username,
:name, :summary, :icon, :image :name, :summary, :icon, :image
has_one :public_key, serializer: ActivityPub::PublicKeySerializer
def id def id
account_url(object) account_url(object)
end end
@ -50,4 +52,8 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
def image def image
full_asset_url(object.header.url(:original)) full_asset_url(object.header.url(:original))
end end
def public_key
object
end
end end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class ActivityPub::BlockSerializer < ActiveModel::Serializer
attributes :type, :actor
attribute :virtual_object, key: :object
def type
'Block'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def virtual_object
ActivityPub::TagManager.instance.uri_for(object.target_account)
end
end

View File

@ -6,8 +6,7 @@ class ActivityPub::CollectionSerializer < ActiveModel::Serializer
super super
end end
attributes :id, :type, :total_items, attributes :id, :type, :total_items
:current
has_many :items, key: :ordered_items has_many :items, key: :ordered_items

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class ActivityPub::DeleteSerializer < ActiveModel::Serializer
attributes :type, :actor
attribute :virtual_object, key: :object
def type
'Delete'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def virtual_object
ActivityPub::TagManager.instance.uri_for(object)
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class ActivityPub::FollowSerializer < ActiveModel::Serializer
attributes :type, :actor
attribute :virtual_object, key: :object
def type
'Follow'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def virtual_object
ActivityPub::TagManager.instance.uri_for(object.target_account)
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class ActivityPub::LikeSerializer < ActiveModel::Serializer
attributes :type, :actor
attribute :virtual_object, key: :object
def type
'Like'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def virtual_object
ActivityPub::TagManager.instance.uri_for(object.status)
end
end

View File

@ -3,7 +3,7 @@
class ActivityPub::NoteSerializer < ActiveModel::Serializer class ActivityPub::NoteSerializer < ActiveModel::Serializer
attributes :id, :type, :summary, :content, attributes :id, :type, :summary, :content,
:in_reply_to, :published, :url, :in_reply_to, :published, :url,
:actor, :to, :cc, :sensitive :attributed_to, :to, :cc, :sensitive
has_many :media_attachments, key: :attachment has_many :media_attachments, key: :attachment
has_many :virtual_tags, key: :tag has_many :virtual_tags, key: :tag
@ -36,7 +36,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer
ActivityPub::TagManager.instance.url_for(object) ActivityPub::TagManager.instance.url_for(object)
end end
def actor def attributed_to
ActivityPub::TagManager.instance.uri_for(object.account) ActivityPub::TagManager.instance.uri_for(object.account)
end end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class ActivityPub::PublicKeySerializer < ActiveModel::Serializer
attributes :id, :owner, :public_key_pem
def id
[ActivityPub::TagManager.instance.uri_for(object), '#main-key'].join
end
def owner
ActivityPub::TagManager.instance.uri_for(object)
end
def public_key_pem
object.public_key
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ActivityPub::RejectFollowSerializer < ActiveModel::Serializer
attributes :type, :actor
has_one :object, serializer: ActivityPub::FollowSerializer
def type
'Reject'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.target_account)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ActivityPub::UndoBlockSerializer < ActiveModel::Serializer
attributes :type, :actor
has_one :object, serializer: ActivityPub::BlockSerializer
def type
'Undo'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ActivityPub::UndoFollowSerializer < ActiveModel::Serializer
attributes :type, :actor
has_one :object, serializer: ActivityPub::FollowSerializer
def type
'Undo'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ActivityPub::UndoLikeSerializer < ActiveModel::Serializer
attributes :type, :actor
has_one :object, serializer: ActivityPub::LikeSerializer
def type
'Undo'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ActivityPub::UpdateSerializer < ActiveModel::Serializer
attributes :type, :actor
has_one :object, serializer: ActivityPub::ActorSerializer
def type
'Update'
end
def actor
ActivityPub::TagManager.instance.uri_for(object)
end
end

View File

@ -9,7 +9,7 @@ class WebPushNotificationWorker
recipient = Account.find(recipient_id) recipient = Account.find(recipient_id)
notification = Notification.find(notification_id) notification = Notification.find(notification_id)
sessions_with_subscriptions = recipient.user.session_activations.reject { |session| session.web_push_subscription.nil? } sessions_with_subscriptions = recipient.user.session_activations.where.not(web_push_subscription: nil)
sessions_with_subscriptions.each do |session| sessions_with_subscriptions.each do |session|
begin begin
@ -17,8 +17,7 @@ class WebPushNotificationWorker
rescue Webpush::InvalidSubscription, Webpush::ExpiredSubscription rescue Webpush::InvalidSubscription, Webpush::ExpiredSubscription
# Subscription expiration is not currently implemented in any browser # Subscription expiration is not currently implemented in any browser
session.web_push_subscription.destroy! session.web_push_subscription.destroy!
session.web_push_subscription = nil session.update!(web_push_subscription: nil)
session.save!
rescue Webpush::PayloadTooLarge => e rescue Webpush::PayloadTooLarge => e
Rails.logger.error(e) Rails.logger.error(e)
end end

View File

@ -85,6 +85,7 @@ Rails.application.configure do
:ca_file => ENV['SMTP_CA_FILE'].presence, :ca_file => ENV['SMTP_CA_FILE'].presence,
:openssl_verify_mode => ENV['SMTP_OPENSSL_VERIFY_MODE'], :openssl_verify_mode => ENV['SMTP_OPENSSL_VERIFY_MODE'],
:enable_starttls_auto => ENV['SMTP_ENABLE_STARTTLS_AUTO'] || true, :enable_starttls_auto => ENV['SMTP_ENABLE_STARTTLS_AUTO'] || true,
:tls => ENV['SMTP_TLS'].presence,
} }
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym

View File

@ -10,7 +10,7 @@ pl:
domain_count_after: instancji domain_count_after: instancji
domain_count_before: Serwer połączony z domain_count_before: Serwer połączony z
features: features:
humane_approach_body: Nauczeni na błędach innych sieci społecznościowych, Mastodon został zaprojektowany tak, aby uniknąć częstych nadużyć. humane_approach_body: Nauczeni na błędach innych sieci społecznościowych, zaprojektowaliśmy Mastodona tak, aby uniknąć częstych nadużyć.
humane_approach_title: Bardziej ludzkie podejście humane_approach_title: Bardziej ludzkie podejście
not_a_product_body: Mastodon nie jest komercyjną siecią. Nie doświadczysz tu reklam, zbierania danych, ani centralnego ośrodka, tak jak w przypadku wielu rozwiązań. not_a_product_body: Mastodon nie jest komercyjną siecią. Nie doświadczysz tu reklam, zbierania danych, ani centralnego ośrodka, tak jak w przypadku wielu rozwiązań.
not_a_product_title: Jesteś człowiekiem, nie produktem not_a_product_title: Jesteś człowiekiem, nie produktem

View File

@ -8,7 +8,7 @@ en:
one: <span class="name-counter">1</span> character left one: <span class="name-counter">1</span> character left
other: <span class="name-counter">%{count}</span> characters left other: <span class="name-counter">%{count}</span> characters left
header: PNG, GIF or JPG. At most 2MB. Will be downscaled to 700x335px header: PNG, GIF or JPG. At most 2MB. Will be downscaled to 700x335px
locked: Requires you to manually approve followers and defaults post privacy to followers-only locked: Requires you to manually approve followers
note: note:
one: <span class="note-counter">1</span> character left one: <span class="note-counter">1</span> character left
other: <span class="note-counter">%{count}</span> characters left other: <span class="note-counter">%{count}</span> characters left

View File

@ -6,7 +6,7 @@ ja:
avatar: 2MBまでのPNGやGIF、JPGが利用可能です。120x120pxまで縮小されます。 avatar: 2MBまでのPNGやGIF、JPGが利用可能です。120x120pxまで縮小されます。
display_name: あと<span class="name-counter">%{count}</span>文字入力できます。 display_name: あと<span class="name-counter">%{count}</span>文字入力できます。
header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます。 header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます。
locked: フォロワーを手動で承認する必要があります。デフォルトではトゥートの公開範囲はフォロワーのみです。 locked: フォロワーを手動で承認する必要があります。
note: あと<span class="note-counter">%{count}</span>文字入力できます。 note: あと<span class="note-counter">%{count}</span>文字入力できます。
imports: imports:
data: 他の Mastodon インスタンスからエクスポートしたCSVファイルを選択して下さい data: 他の Mastodon インスタンスからエクスポートしたCSVファイルを選択して下さい

View File

@ -10,7 +10,7 @@ pl:
one: Pozostał <span class="name-counter">1</span> znak. one: Pozostał <span class="name-counter">1</span> znak.
other: 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 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 locked: Musisz akceptować prośby o śledzenie
note: note:
few: Pozostały <span class="name-counter">%{count}</span> znaki. few: Pozostały <span class="name-counter">%{count}</span> znaki.
many: Pozostało <span class="name-counter">%{count}</span> znaków many: Pozostało <span class="name-counter">%{count}</span> znaków

View File

@ -232,9 +232,16 @@ namespace :mastodon do
task prepare_for_foreign_keys: :environment do task prepare_for_foreign_keys: :environment do
# All the deletes: # All the deletes:
ActiveRecord::Base.connection.execute('DELETE FROM statuses USING statuses s LEFT JOIN accounts a ON a.id = s.account_id WHERE statuses.id = s.id AND a.id IS NULL') ActiveRecord::Base.connection.execute('DELETE FROM statuses USING statuses s LEFT JOIN accounts a ON a.id = s.account_id WHERE statuses.id = s.id AND a.id IS NULL')
ActiveRecord::Base.connection.execute('DELETE FROM account_domain_blocks USING account_domain_blocks adb LEFT JOIN accounts a ON a.id = adb.account_id WHERE account_domain_blocks.id = adb.id AND a.id IS NULL')
ActiveRecord::Base.connection.execute('DELETE FROM conversation_mutes USING conversation_mutes cm LEFT JOIN accounts a ON a.id = cm.account_id WHERE conversation_mutes.id = cm.id AND a.id IS NULL') if ActiveRecord::Base.connection.table_exists? :account_domain_blocks
ActiveRecord::Base.connection.execute('DELETE FROM conversation_mutes USING conversation_mutes cm LEFT JOIN conversations c ON c.id = cm.conversation_id WHERE conversation_mutes.id = cm.id AND c.id IS NULL') ActiveRecord::Base.connection.execute('DELETE FROM account_domain_blocks USING account_domain_blocks adb LEFT JOIN accounts a ON a.id = adb.account_id WHERE account_domain_blocks.id = adb.id AND a.id IS NULL')
end
if ActiveRecord::Base.connection.table_exists? :conversation_mutes
ActiveRecord::Base.connection.execute('DELETE FROM conversation_mutes USING conversation_mutes cm LEFT JOIN accounts a ON a.id = cm.account_id WHERE conversation_mutes.id = cm.id AND a.id IS NULL')
ActiveRecord::Base.connection.execute('DELETE FROM conversation_mutes USING conversation_mutes cm LEFT JOIN conversations c ON c.id = cm.conversation_id WHERE conversation_mutes.id = cm.id AND c.id IS NULL')
end
ActiveRecord::Base.connection.execute('DELETE FROM favourites USING favourites f LEFT JOIN accounts a ON a.id = f.account_id WHERE favourites.id = f.id AND a.id IS NULL') ActiveRecord::Base.connection.execute('DELETE FROM favourites USING favourites f LEFT JOIN accounts a ON a.id = f.account_id WHERE favourites.id = f.id AND a.id IS NULL')
ActiveRecord::Base.connection.execute('DELETE FROM favourites USING favourites f LEFT JOIN statuses s ON s.id = f.status_id WHERE favourites.id = f.id AND s.id IS NULL') ActiveRecord::Base.connection.execute('DELETE FROM favourites USING favourites f LEFT JOIN statuses s ON s.id = f.status_id WHERE favourites.id = f.id AND s.id IS NULL')
ActiveRecord::Base.connection.execute('DELETE FROM blocks USING blocks b LEFT JOIN accounts a ON a.id = b.account_id WHERE blocks.id = b.id AND a.id IS NULL') ActiveRecord::Base.connection.execute('DELETE FROM blocks USING blocks b LEFT JOIN accounts a ON a.id = b.account_id WHERE blocks.id = b.id AND a.id IS NULL')