Add Misskey location, birthday, and isCat

This commit is contained in:
noellabo 2022-02-10 05:47:09 +09:00
parent 1128e473c6
commit 1e6fbdea3a
41 changed files with 320 additions and 57 deletions

View file

@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
private
def account_params
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, :birthday, :location, fields_attributes: [:name, :value])
end
def user_settings_params

View file

@ -76,6 +76,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_hide_statuses_count,
:setting_hide_following_count,
:setting_hide_followers_count,
:setting_disable_joke_appearance,
notification_emails: %i(follow follow_request reblog favourite emoji_reaction mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)

View file

@ -20,7 +20,7 @@ class Settings::ProfilesController < Settings::BaseController
private
def account_params
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, :birthday, :location, fields_attributes: [:name, :value])
end
def set_account

View file

@ -98,6 +98,26 @@ module AccountsHelper
[prepend_str, account.note].join(' · ')
end
def account_cat_params(account, **options)
result = options || {}
result.merge!({ 'data-acct': account.acct })
return result unless !current_user&.setting_disable_joke_appearance && account.other_settings['is_cat']
@cat_inline_styles ||= {}
@cat_inline_styles[account.acct] = account.cat_ears_color if account.cat_ears_color
result.merge!({ class: [options[:class], 'cat'].compact.join(' ') })
end
def account_cat_styles
return if @cat_inline_styles.nil?
@cat_inline_styles.map do |acct, color|
".cat[data-acct=\"#{h(acct)}\"] { --cat-ears-color: #{h(color)}; }"
end.join("\n")
end
def svg_logo
content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976')
end

View file

@ -27,6 +27,8 @@ module ContextHelper
quote_uri: { 'fedibird' => 'http://fedibird.com/ns#', 'quoteUri' => 'fedibird:quoteUri' },
expiry: { 'fedibird' => 'http://fedibird.com/ns#', 'expiry' => 'fedibird:expiry' },
other_setting: { 'fedibird' => 'http://fedibird.com/ns#', 'otherSetting' => 'fedibird:otherSetting' },
is_cat: { 'misskey' => 'https://misskey-hub.net/ns#', 'isCat' => 'misskey:isCat' },
vcard: { 'vcard' => 'http://www.w3.org/2006/vcard/ns#' },
}.freeze
def full_context

View file

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { autoPlayGif } from '../initial_state';
import classNames from 'classnames';
import { autoPlayGif, disable_joke_appearance } from '../initial_state';
export default class Avatar extends React.PureComponent {
@ -38,19 +39,18 @@ export default class Avatar extends React.PureComponent {
const { hovering } = this.state;
const src = account.get('avatar');
const isCat = !disable_joke_appearance && account.getIn(['other_settings', 'is_cat']);
const catEarsColor = !disable_joke_appearance && account.getIn(['other_settings', 'cat_ears_color']);
const staticSrc = account.get('avatar_static');
let className = 'account__avatar';
if (inline) {
className = className + ' account__avatar-inline';
}
const catEarsColorStyle = catEarsColor ? { '--cat-ears-color': catEarsColor } : {};
const style = {
...this.props.style,
width: `${size}px`,
height: `${size}px`,
backgroundSize: `${size}px ${size}px`,
...catEarsColorStyle,
};
if (hovering || animate) {
@ -61,7 +61,8 @@ export default class Avatar extends React.PureComponent {
return (
<div
className={className}
className={classNames('account__avatar', { 'account__avatar-inline': inline, 'account__avatar-cat': isCat })}
data-cat-ears-color={catEarsColor}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={style}

View file

@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
import Button from 'mastodon/components/button';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { autoPlayGif, me, isStaff, show_followed_by, follow_button_to_list_adder } from 'mastodon/initial_state';
@ -14,6 +14,7 @@ import ShortNumber from 'mastodon/components/short_number';
import { NavLink } from 'react-router-dom';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import AccountNoteContainer from '../containers/account_note_container';
import age from 's-age';
const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@ -338,6 +339,10 @@ class Header extends ImmutablePureComponent {
const hide_following_count = account.getIn(['other_settings', 'hide_following_count'], false);
const hide_followers_count = account.getIn(['other_settings', 'hide_followers_count'], false);
const location = account.getIn(['other_settings', 'location']);
const birthday = account.getIn(['other_settings', 'birthday']);
const joined = account.get('created_at');
return (
<div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<div className='account__header__image'>
@ -408,7 +413,24 @@ class Header extends ImmutablePureComponent {
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />}
<div className='account__header__joined'><FormattedMessage id='account.joined' defaultMessage='Joined {date}' values={{ date: intl.formatDate(account.get('created_at'), { year: 'numeric', month: 'short', day: '2-digit' }) }} /></div>
<div className='account__header__personal--wrapper'>
<table className='account__header__personal'>
<tbody>
{location && <tr>
<th><Icon id='map-marker' fixedWidth aria-hidden='true' /> <FormattedMessage id='account.location' defaultMessage='Location' /></th>
<td>{location}</td>
</tr>}
{birthday && <tr>
<th><Icon id='birthday-cake' fixedWidth aria-hidden='true' /> <FormattedMessage id='account.birthday' defaultMessage='Birthday' /></th>
<td><FormattedDate value={birthday} hour12={false} year='numeric' month='short' day='2-digit' />(<FormattedMessage id='account.age' defaultMessage='{age} years old}' values={{age: age(birthday)}} />)</td>
</tr>}
<tr>
<th><Icon id='calendar' fixedWidth aria-hidden='true' /> <FormattedMessage id='account.joined' defaultMessage='Joined' /></th>
<td><FormattedDate value={joined} hour12={false} year='numeric' month='short' day='2-digit' /></td>
</tr>
</tbody>
</table>
</div>
</div>
{!suspended && (

View file

@ -41,5 +41,6 @@ export const show_tab_bar_label = getMeta('show_tab_bar_label');
export const enable_limited_timeline = getMeta('enable_limited_timeline');
export const enableReaction = getMeta('enable_reaction');
export const show_reply_tree_button = getMeta('show_reply_tree_button');
export const disable_joke_appearance = getMeta('disable_joke_appearance');
export default initialState;

View file

@ -2,8 +2,10 @@
"account.account_note_header": "Note",
"account.add_or_remove_from_circle": "Add or Remove from circles",
"account.add_or_remove_from_list": "Add or Remove from lists",
"account.age": "{age} years old",
"account.badges.bot": "Bot",
"account.badges.group": "Group",
"account.birthday": "Birthday",
"account.block": "Block @{name}",
"account.block_domain": "Block domain {domain}",
"account.blocked": "Blocked",
@ -24,9 +26,10 @@
"account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}",
"account.joined": "Joined {date}",
"account.joined": "Joined",
"account.last_status": "Last active",
"account.link_verified_on": "Ownership of this link was checked on {date}",
"account.location": "Location",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media",
"account.members": "Members",

View file

@ -2,8 +2,10 @@
"account.account_note_header": "メモ",
"account.add_or_remove_from_circle": "サークルから追加または外す",
"account.add_or_remove_from_list": "リストから追加または外す",
"account.age": "{age}歳",
"account.badges.bot": "Bot",
"account.badges.group": "Group",
"account.birthday": "誕生日",
"account.block": "@{name}さんをブロック",
"account.block_domain": "{domain}全体をブロック",
"account.blocked": "ブロック済み",
@ -24,9 +26,10 @@
"account.follows.empty": "まだ誰もフォローしていません。",
"account.follows_you": "フォローされています",
"account.hide_reblogs": "@{name}さんからのブーストを非表示",
"account.joined": "{date} に登録",
"account.joined": "登録",
"account.last_status": "最後の活動",
"account.link_verified_on": "このリンクの所有権は{date}に確認されました",
"account.location": "場所",
"account.locked_info": "このアカウントは承認制アカウントです。相手が承認するまでフォローは完了しません。",
"account.media": "メディア",
"account.members": "参加者",

View file

@ -1608,7 +1608,7 @@ a .account__avatar {
bottom: 0;
right: 0;
z-index: 1;
img {
@include avatar-radius;
width: 100%;
@ -7447,6 +7447,24 @@ noscript {
}
}
.account__header__personal {
font-size: 14px;
color: $darker-text-color;
margin: 5px auto;
border-collapse: separate;
border-spacing: 10px 3px;
th {
white-space: nowrap;
}
&--wrapper {
width: 100%;
margin-top: 10px;
border-top: 1px solid lighten($ui-base-color, 12%);
}
}
.account__header__fields {
margin: 0;
border-top: 1px solid lighten($ui-base-color, 12%);
@ -7472,6 +7490,7 @@ noscript {
font-size: 14px;
color: $darker-text-color;
padding: 10px 0;
border-top: 1px solid lighten($ui-base-color, 12%);
a {
display: inline-block;

View file

@ -296,6 +296,10 @@ code {
max-width: 50%;
}
&-8 {
max-width: 66.67%;
}
.actions {
margin-top: 27px;
}

View file

@ -6,6 +6,15 @@ module ActivityPub::CaseTransform
@camel_lower_cache ||= {}
end
NON_CONVERSIONS = %w(
_misskey_content
_misskey_quote
_misskey_reaction
_misskey_votes
_misskey_talk
vcard:Address
).freeze
def camel_lower(value)
case value
when Array then value.map { |item| camel_lower(item) }
@ -14,7 +23,7 @@ module ActivityPub::CaseTransform
when String
camel_lower_cache[value] ||= if value.start_with?('_:')
'_:' + value.gsub(/\A_:/, '').underscore.camelize(:lower)
elsif value.start_with?('_')
elsif NON_CONVERSIONS.include? value
value
else
value.underscore.camelize(:lower)

View file

@ -28,6 +28,7 @@ class Formatter
html = reformat(raw_content)
html = apply_inner_link(html)
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
html = nyaize_html(html) if options[:nyaize]
return html.html_safe # rubocop:disable Rails/OutputSafety
end
@ -40,6 +41,7 @@ class Formatter
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
html = simple_format(html, {}, sanitize: false)
html = quotify(html, status) if status.quote? && !options[:escape_quotify]
html = nyaize_html(html) if options[:nyaize]
html = html.delete("\n")
html.html_safe # rubocop:disable Rails/OutputSafety
@ -210,6 +212,44 @@ class Formatter
html.sub(/(<[^>]+>)\z/, "<span class=\"quote-inline\"><br/>QT: #{link}</span>\\1")
end
def nyaize_html(html)
inside_anchor = false
html.split(/(<.+?>)/).compact.map do |x|
if x.match(/^<a/)
inside_anchor = true
elsif x == '</a>'
inside_anchor = false
end
if inside_anchor || x[0] == '<'
x
else
x.split(/(:.+?:)/).compact.map do |x|
if x[0] == ':'
x
else
nyaize(x)
end
end.join
end
end.join
end
def nyaize(text)
text
# ja-JP
.gsub(//, "にゃ").gsub(//, "ニャ").gsub(//, "ニャ")
# en-US
.gsub(/(?<=n)a/i) { |x| x == 'A' ? 'YA' : 'ya' }
.gsub(/(?<=morn)ing/i) { |x| x == 'ING' ? 'YAN' : 'yan' }
.gsub(/(?<=every)one/i) { |x| x == 'ONE' ? 'NYAN' : 'nyan' }
# vko-KR
.gsub(/[나-낳]/) { |c| (c.ord + '냐'.ord - '나'.ord).chr }
.gsub(/(다)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/m, '다냥')
.gsub(/(야(?=\?))|(야$)|(야(?= ))/m, '냥')
end
def rewrite(text, entities)
text = text.to_s

View file

@ -70,6 +70,7 @@ class UserSettingsDecorator
user.settings['hide_statuses_count'] = hide_statuses_count_preference if change?('setting_hide_statuses_count')
user.settings['hide_following_count'] = hide_following_count_preference if change?('setting_hide_following_count')
user.settings['hide_followers_count'] = hide_followers_count_preference if change?('setting_hide_followers_count')
user.settings['disable_joke_appearance'] = disable_joke_appearance_preference if change?('setting_disable_joke_appearance')
end
def merged_notification_emails
@ -236,6 +237,10 @@ class UserSettingsDecorator
boolean_cast_setting 'setting_hide_followers_count'
end
def disable_joke_appearance_preference
boolean_cast_setting 'setting_disable_joke_appearance'
end
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end

View file

@ -414,23 +414,6 @@ class Account < ApplicationRecord
ActionController::Base.helpers.strip_tags(note)
end
def settings
self[:settings].class == String ? {} : self[:settings]
end
def other_settings
local? && user ? settings.merge(
{
'noindex' => user.setting_noindex,
'hide_network' => user.setting_hide_network,
'hide_statuses_count' => user.setting_hide_statuses_count,
'hide_following_count' => user.setting_hide_following_count,
'hide_followers_count' => user.setting_hide_followers_count,
'enable_reaction' => user.setting_enable_reaction,
}
) : settings
end
class Field < ActiveModelSerializers::Model
attributes :name, :value, :verified_at, :account

View file

@ -6,9 +6,14 @@ module AccountAvatar
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
LIMIT = 2.megabytes
BLURHASH_OPTIONS = {
x_comp: 4,
y_comp: 4,
}.freeze
class_methods do
def avatar_styles(file)
styles = { original: { geometry: '400x400#', file_geometry_parser: FastGeometryParser } }
styles = { original: { geometry: '400x400#', file_geometry_parser: FastGeometryParser, blurhash: BLURHASH_OPTIONS } }
styles[:static] = { geometry: '400x400#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
styles
end
@ -18,7 +23,7 @@ module AccountAvatar
included do
# Avatar upload
has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '+profile exif' }, processors: [:lazy_thumbnail]
has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '+profile exif' }, processors: [:lazy_thumbnail, :blurhash_transcoder]
validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
validates_attachment_size :avatar, less_than: LIMIT
remotable_attachment :avatar, LIMIT, suppress_errors: false

View file

@ -3,24 +3,81 @@
module AccountSettings
extend ActiveSupport::Concern
included do
after_initialize :setting_initialize
end
def cat?
true & settings['is_cat']
end
alias cat cat?
def cat=(val)
settings['is_cat'] = true & ActiveModel::Type::Boolean.new.cast(val)
end
def cat_ears_color
settings['cat_ears_color']
end
def birthday
settings['birthday']
end
def birthday=(val)
settings['birthday'] = ActiveRecord::Type::Date.new.cast(val)
end
def location
settings['location']
end
def location=(val)
settings['location'] = val
end
def noindex?
local? ? user&.noindex? : settings['noindex']
true & (local? ? user&.noindex? : settings['noindex'])
end
def hide_network?
local? ? user&.hide_network? : settings['hide_network']
true & (local? ? user&.hide_network? : settings['hide_network'])
end
def hide_statuses_count?
local? ? user&.hide_statuses_count? : settings['hide_statuses_count']
true & (local? ? user&.hide_statuses_count? : settings['hide_statuses_count'])
end
def hide_following_count?
local? ? user&.hide_following_count? : settings['hide_following_count']
true & (local? ? user&.hide_following_count? : settings['hide_following_count'])
end
def hide_followers_count?
local? ? user&.hide_followers_count? : settings['hide_followers_count']
true & (local? ? user&.hide_followers_count? : settings['hide_followers_count'])
end
def other_settings
local? && user ? settings.merge(
{
'noindex' => user.setting_noindex,
'hide_network' => user.setting_hide_network,
'hide_statuses_count' => user.setting_hide_statuses_count,
'hide_following_count' => user.setting_hide_following_count,
'hide_followers_count' => user.setting_hide_followers_count,
'enable_reaction' => user.setting_enable_reaction,
}
) : settings
end
# Called by blurhash_transcoder
def blurhash=(val)
settings['cat_ears_color'] = "##{Blurhash::Base83::decode83(val.slice(2,4)).to_s(16).rjust(6, '0')}"
end
private
def setting_initialize
self[:settings] = {} if has_attribute?(:settings) && self[:settings] === "{}"
end
end

View file

@ -131,7 +131,7 @@ class User < ApplicationRecord
:follow_button_to_list_adder, :show_navigation_panel, :show_quote_button, :show_bookmark_button,
:place_tab_bar_at_bottom,:show_tab_bar_label, :enable_limited_timeline, :enable_reaction,
:show_reply_tree_button,
:hide_statuses_count, :hide_following_count, :hide_followers_count,
:hide_statuses_count, :hide_following_count, :hide_followers_count, :disable_joke_appearance,
to: :settings, prefix: :setting, allow_nil: false

View file

@ -7,7 +7,8 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
context_extensions :manually_approves_followers, :featured, :also_known_as,
:moved_to, :property_value, :identity_proof,
:discoverable, :olm, :suspended, :other_setting
:discoverable, :olm, :suspended, :other_setting,
:vcard
attributes :id, :type, :following, :followers,
:inbox, :outbox, :featured, :featured_tags,
@ -24,6 +25,8 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
attribute :moved_to, if: :moved?
attribute :also_known_as, if: :also_known_as?
attribute :suspended, if: :suspended?
attribute :bday, key: :'vcard:bday'
attribute :address, key: :'vcard:Address'
has_many :virtual_other_settings, key: :other_setting
@ -164,6 +167,14 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
object.created_at.midnight.iso8601
end
def bday
object.birthday
end
def address
object.location
end
def virtual_other_settings
object.other_settings.map do |k, v|
{

View file

@ -55,6 +55,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:enable_limited_timeline] = object.current_account.user.setting_enable_limited_timeline
store[:enable_reaction] = object.current_account.user.setting_enable_reaction
store[:show_reply_tree_button] = object.current_account.user.setting_show_reply_tree_button
store[:disable_joke_appearance] = object.current_account.user.setting_disable_joke_appearance
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media

View file

@ -3,7 +3,7 @@
class REST::AccountSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
attributes :id, :username, :acct, :display_name, :locked, :bot, :cat, :discoverable, :group, :created_at,
:note, :url, :avatar, :avatar_static, :header, :header_static,
:followers_count, :following_count, :subscribing_count, :statuses_count, :last_status_at
@ -88,6 +88,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
object.suspended? ? false : object.bot
end
def cat
object.suspended? ? false : object.cat
end
def discoverable
object.suspended? ? false : object.discoverable
end

View file

@ -127,6 +127,8 @@ class REST::InstanceSerializer < ActiveModel::Serializer
:visibility_mutual,
:visibility_limited,
:emoji_reaction,
:misskey_birthday,
:misskey_location,
]
end

View file

@ -17,6 +17,8 @@ class REST::StatusSerializer < ActiveModel::Serializer
attribute :content, unless: :source_requested?
attribute :text, if: :source_requested?
attribute :nyaize_content, if: :joke_applied?
attribute :quote_id, if: :quote?
attribute :expires_at, if: :has_expires?
@ -119,7 +121,11 @@ class REST::StatusSerializer < ActiveModel::Serializer
end
def content
Formatter.instance.format(object)
@content ||= Formatter.instance.format(object)
end
def nyaize_content
@nyaize_content ||= Formatter.instance.format(object, nyaize: object.account.cat?)
end
def url
@ -188,6 +194,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
instance_options[:source_requested]
end
def joke_applied?
!source_requested? && object.account.cat? && nyaize_content != content
end
def ordered_mentions
object.active_mentions.to_a.sort_by(&:id)
end

View file

@ -98,7 +98,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account.note = @json['summary'] || ''
@account.locked = @json['manuallyApprovesFollowers'] || false
@account.fields = property_values || {}
@account.settings = defer_settings.merge(other_settings || {})
@account.settings = defer_settings.merge(other_settings, birthday, address, is_cat)
@account.also_known_as = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) }
@account.discoverable = @json['discoverable'] || false
end
@ -212,16 +212,34 @@ class ActivityPub::ProcessAccountService < BaseService
as_array(@json['attachment']).select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
end
def birthday
return {} if @json['vcard:bday'].blank?
{ 'birthday' => ActiveRecord::Type::Date.new.cast(@json['vcard:bday']) }
end
def address
return {} if @json['vcard:Address'].blank?
{ 'location' => @json['vcard:Address'] }
end
def is_cat
return {} unless ActiveModel::Type::Boolean.new.cast(@json['isCat'])
{ 'is_cat' => true }
end
DEFER_SETTINGS_KEYS = %w(
birthday
location
cat_ears_color
noindex
).freeze
def defer_settings
(@account.settings || {}).select { |key, _| DEFER_SETTINGS_KEYS.include?(key) }
@account.settings.select { |key, _| DEFER_SETTINGS_KEYS.include?(key) }
end
def other_settings
return unless @json['otherSetting'].is_a?(Array)
return {} unless @json['otherSetting'].is_a?(Array)
@json['otherSetting'].each_with_object({}) { |v, h| h.merge!({v['name'] => v['value']}) if v['type'] == 'PropertyValue' }
end

View file

@ -2,7 +2,7 @@
.public-account-header__image
= image_tag (prefers_autoplay? ? account.header_original_url : account.header_static_url), class: 'parallax'
.public-account-header__bar
= link_to short_account_url(account), class: 'avatar' do
= link_to short_account_url(account), account_cat_params(account, class: 'avatar') do
= image_tag (prefers_autoplay? ? account.avatar_original_url : account.avatar_static_url), id: 'profile_page_avatar', data: { original: full_asset_url(account.avatar_original_url), static: full_asset_url(account.avatar_static_url), autoplay: prefers_autoplay? }
.public-account-header__tabs
.public-account-header__tabs__name

View file

@ -5,7 +5,7 @@
.card__img
= image_tag account.header.url, alt: ''
.card__bar
.avatar
.avatar{ account_cat_params(account) }
= image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo'
.display-name

View file

@ -24,7 +24,7 @@
= image_tag account.header.url, alt: ''
.directory__card__bar
= link_to TagManager.instance.url_for(account), class: 'directory__card__bar__name' do
.avatar
.avatar{ account_cat_params(account) }
= image_tag account.avatar.url, alt: '', class: 'u-photo'
.display-name

View file

@ -33,6 +33,9 @@
- if Setting.custom_css.present?
= stylesheet_link_tag custom_css_path, host: request.host, media: 'all'
%style{ nonce: request.content_security_policy_nonce }
!= account_cat_styles
= yield :header_tags
%body{ class: body_classes }

View file

@ -33,6 +33,11 @@
= f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label
= f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
%h4= t 'appearance.joke'
.fields-group
= f.input :setting_disable_joke_appearance, as: :boolean, wrapper: :with_label, fedibird_features: true
%h4= t 'appearance.toot_layout'
.fields-group

View file

@ -55,6 +55,16 @@
%input{ type: :text, maxlength: '999', spellcheck: 'false', readonly: 'true', value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: 'me').to_str }
%button{ type: :button }= t('generic.copy')
.fields-row
.fields-row__column.fields-group.fields-row__column-8
= f.input :birthday, wrapper: :with_label, input_html: { placeholder: '2016-03-16', pattern: '\d{4}-\d{1,2}-\d{1,2}' }, hint: t('simple_form.hints.defaults.birthday'), fedibird_features: true
%p.warning-hint= t('simple_form.hints.defaults.birthday_caution')
.fields-row
.fields-row__column.fields-group.fields-row__column-8
= f.input :location, wrapper: :with_label, input_html: { maxlength: 50 }, hint: t('simple_form.hints.defaults.location'), fedibird_features: true
%p.warning-hint= t('simple_form.hints.defaults.location_caution')
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -1,7 +1,7 @@
.detailed-status.detailed-status--flex{ class: "detailed-status-#{status.visibility}" }
.p-author.h-card
= link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'detailed-status__display-name u-url', target: stream_link_target, rel: 'noopener' do
.detailed-status__display-avatar
.detailed-status__display-avatar{ account_cat_params(status.account) }
- if prefers_autoplay?
= image_tag status.account.avatar_original_url, alt: '', class: 'account__avatar u-photo'
- else

View file

@ -1,6 +1,6 @@
.status.quote-status{ dataurl: ActivityPub::TagManager.instance.url_for(status) }
= link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener' do
.status__avatar
.status__avatar{ account_cat_params(status.account) }
%div
= image_tag status.account.avatar_static_url, width: 18, height: 18, alt: '', class: 'u-photo account__avatar'
%span.display-name

View file

@ -11,12 +11,11 @@
.p-author.h-card
= link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener noreferrer' do
.status__avatar
%div
- if prefers_autoplay?
= image_tag status.account.avatar_original_url, alt: '', class: 'u-photo account__avatar'
- else
= image_tag status.account.avatar_static_url, alt: '', class: 'u-photo account__avatar'
.status__avatar{ account_cat_params(status.account) }
- if prefers_autoplay?
= image_tag status.account.avatar_original_url, alt: '', class: 'u-photo account__avatar'
- else
= image_tag status.account.avatar_static_url, alt: '', class: 'u-photo account__avatar'
%span.display-name
%bdi
%strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: prefers_autoplay?)

View file

@ -762,6 +762,7 @@ en:
animations_and_accessibility: Animations and accessibility
confirmation_dialogs: Confirmation dialogs
discovery: Discovery
joke: Joke features
localization:
body: Mastodon is translated by volunteers.
guide_link: https://crowdin.com/project/mastodon

View file

@ -740,6 +740,7 @@ ja:
animations_and_accessibility: アニメーションとアクセシビリティー
confirmation_dialogs: 確認ダイアログ
discovery: 見つける
joke: ジョーク機能
localization:
body: Mastodonは有志によって翻訳されています。
guide_link: https://ja.crowdin.com/project/mastodon

View file

@ -31,6 +31,8 @@ en:
defaults:
autofollow: People who sign up through the invite will automatically follow you
avatar: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px
birthday: Specify the date of birth in the format yyyy-mm-ddex. 2016-03-16
birthday_caution: It will be published on the Internet. Please handle your personal information with care
bot: Signal to others that the account mainly performs automated actions and might not be monitored
context: One or multiple contexts where the filter should apply
current_password: For security purposes please enter the password of the current account
@ -43,12 +45,15 @@ en:
inbox_url: Copy the URL from the frontpage of the relay you want to use
irreversible: Filtered posts will disappear irreversibly, even if filter is later removed
locale: The language of the user interface, e-mails and push notifications
location: Area of primary residence or activity
location_caution: It will be published on the Internet. Please do not include detailed addresses except for business use
locked: Manually control who can follow you by approving follow requests
password: Use at least 8 characters
phrase: Will be matched regardless of casing in text or content warning of a post
scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones.
setting_aggregate_reblogs: Do not show new boosts for posts that have been recently boosted (only affects newly-received boosts)
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
setting_disable_joke_appearance: Disable April Fools' Day and other joke functions
setting_display_media_default: Hide media marked as sensitive
setting_display_media_hide_all: Always hide media
setting_display_media_show_all: Always show media
@ -155,6 +160,7 @@ en:
defaults:
autofollow: Invite to follow your account
avatar: Avatar
birthday: Birthday
bot: This is a bot account
chosen_languages: Filter languages
confirm_new_password: Confirm new password
@ -172,6 +178,7 @@ en:
inbox_url: URL of the relay inbox
irreversible: Drop instead of hide
locale: Interface language
location: Location
locked: Require follow requests
max_uses: Max number of uses
new_password: New password
@ -188,6 +195,7 @@ en:
setting_default_privacy: Posting privacy
setting_default_sensitive: Always mark media as sensitive
setting_delete_modal: Show confirmation dialog before deleting a post
setting_disable_joke_appearance: Disable joke feature to change appearance
setting_disable_swiping: Disable swiping motions
setting_display_media: Media display
setting_display_media_default: Default

View file

@ -31,6 +31,8 @@ ja:
defaults:
autofollow: 招待から登録した人が自動的にあなたをフォローするようになります
avatar: "%{size}までのPNG、GIF、JPGが利用可能です。%{dimensions}pxまで縮小されます"
birthday: 誕生日を年月日の順にyyyy-mm-ddex. 2016-03-16という書式で指定します
birthday_caution: インターネットに公開されます。個人情報は慎重に取り扱ってください
bot: このアカウントは主に自動で動作し、人が見ていない可能性があります
context: フィルターを適用する対象 (複数選択可)
current_password: 現在のアカウントのパスワードを入力してください
@ -43,12 +45,15 @@ ja:
inbox_url: 使用したいリレーサーバーのトップページからURLをコピーします
irreversible: フィルターが後で削除されても、除外された投稿は元に戻せなくなります
locale: ユーザーインターフェース、メールやプッシュ通知の言語
location: 主に居住・活動する地域・場所
location_caution: インターネットに公開されます。ビジネス用途を除き詳細な住所を記載しないでください
locked: フォロワーを手動で承認する必要があります
password: 少なくとも8文字は入力してください
phrase: 投稿内容の大文字小文字や閲覧注意に関係なく一致
scopes: アプリの API に許可するアクセス権を選択してください。最上位のスコープを選択する場合、個々のスコープを選択する必要はありません。
setting_aggregate_reblogs: 最近ブーストされた投稿が新たにブーストされても表示しません (設定後受信したものにのみ影響)
setting_default_sensitive: 閲覧注意状態のメディアはデフォルトでは内容が伏せられ、クリックして初めて閲覧できるようになります
setting_disable_joke_appearance: エイプリルフール等のジョーク機能を無効にします
setting_display_media_default: 閲覧注意としてマークされたメディアは隠す
setting_display_media_hide_all: メディアを常に隠す
setting_display_media_show_all: メディアを常に表示する
@ -155,6 +160,7 @@ ja:
defaults:
autofollow: 招待から参加後、あなたをフォロー
avatar: アイコン
birthday: 誕生日
bot: これは BOT アカウントです
chosen_languages: 表示する言語
confirm_new_password: 新しいパスワード(確認用)
@ -172,6 +178,7 @@ ja:
inbox_url: リレーサーバーの inbox URL
irreversible: 隠すのではなく除外する
locale: 言語
location: 場所
locked: 承認制アカウントにする
max_uses: 使用できる回数
new_password: 新しいパスワード
@ -188,6 +195,7 @@ ja:
setting_default_privacy: 投稿の公開範囲
setting_default_sensitive: メディアを常に閲覧注意としてマークする
setting_delete_modal: 投稿を削除する前に確認ダイアログを表示する
setting_disable_joke_appearance: ジョーク機能による見た目の変更を無効にする
setting_disable_swiping: スワイプでの切り替えを無効にする
setting_display_media: メディアの表示
setting_display_media_default: 標準

View file

@ -55,6 +55,7 @@ defaults: &defaults
hide_statuses_count: false
hide_following_count: false
hide_followers_count: false
disable_joke_appearance: false
notification_emails:
follow: false
reblog: false

View file

@ -153,6 +153,7 @@
"requestidlecallback": "^0.3.0",
"reselect": "^4.0.0",
"rimraf": "^3.0.2",
"s-age": "^1.1.2",
"sass": "^1.37.0",
"sass-loader": "^10.2.0",
"stacktrace-js": "^2.0.2",

View file

@ -9869,6 +9869,11 @@ rx-lite@^3.1.2:
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=
s-age@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/s-age/-/s-age-1.1.2.tgz#c0cf15233ccc93f41de92ea42c36d957977d1ea2"
integrity sha512-aSN2TlF39WLoZA/6cgYSJZhKt63kJ4EaadejPWjWY9/h4rksIqvfWY3gfd+3uAegSM1IXsA9aWeEhJtkxkFQtA==
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"