Merge branch 'stable-4.3' into glitch-soc/backports-4.3

Conflicts:
- `app/helpers/application_helper.rb`:
  Upstream added a helper where glitch-soc had its own, not really
  a conflict.
  Added upstream's helper.
This commit is contained in:
Claire 2024-10-05 20:45:05 +02:00
commit dfe851b476
133 changed files with 777 additions and 316 deletions

View file

@ -50,7 +50,7 @@ jobs:
# Create or update the pull request # Create or update the pull request
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v7.0.1 uses: peter-evans/create-pull-request@v7.0.5
with: with:
commit-message: 'New Crowdin translations' commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)' title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)'

View file

@ -53,7 +53,7 @@ jobs:
# Create or update the pull request # Create or update the pull request
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v7.0.1 uses: peter-evans/create-pull-request@v7.0.5
with: with:
commit-message: 'New Crowdin translations' commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations (automated)' title: 'New Crowdin Translations (automated)'

View file

@ -601,7 +601,7 @@ GEM
actionmailer (>= 3) actionmailer (>= 3)
net-smtp net-smtp
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
propshaft (1.0.1) propshaft (1.1.0)
actionpack (>= 7.0.0) actionpack (>= 7.0.0)
activesupport (>= 7.0.0) activesupport (>= 7.0.0)
rack rack
@ -698,7 +698,7 @@ GEM
responders (3.1.1) responders (3.1.1)
actionpack (>= 5.2) actionpack (>= 5.2)
railties (>= 5.2) railties (>= 5.2)
rexml (3.3.7) rexml (3.3.8)
rotp (6.3.0) rotp (6.3.0)
rouge (4.3.0) rouge (4.3.0)
rpam2 (4.0.2) rpam2 (4.0.2)
@ -748,15 +748,15 @@ GEM
parser (>= 3.3.1.0) parser (>= 3.3.1.0)
rubocop-capybara (2.21.0) rubocop-capybara (2.21.0)
rubocop (~> 1.41) rubocop (~> 1.41)
rubocop-performance (1.21.1) rubocop-performance (1.22.1)
rubocop (>= 1.48.1, < 2.0) rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails (2.25.1) rubocop-rails (2.26.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.52.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (3.0.4) rubocop-rspec (3.0.5)
rubocop (~> 1.61) rubocop (~> 1.61)
rubocop-rspec_rails (2.30.0) rubocop-rspec_rails (2.30.0)
rubocop (~> 1.61) rubocop (~> 1.61)
@ -884,7 +884,7 @@ GEM
webfinger (1.2.0) webfinger (1.2.0)
activesupport activesupport
httpclient (>= 2.4) httpclient (>= 2.4)
webmock (3.23.1) webmock (3.24.0)
addressable (>= 2.8.0) addressable (>= 2.8.0)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)

View file

@ -13,7 +13,7 @@ module WebAppControllerConcern
policy = ContentSecurityPolicy.new policy = ContentSecurityPolicy.new
if policy.sso_host.present? if policy.sso_host.present?
p.form_action policy.sso_host p.form_action policy.sso_host, -> { "https://#{request.host}/auth/auth/" }
else else
p.form_action :none p.form_action :none
end end

View file

@ -15,7 +15,7 @@ module Settings
end end
def create def create
session[:new_otp_secret] = User.generate_otp_secret(32) session[:new_otp_secret] = User.generate_otp_secret
redirect_to new_settings_two_factor_authentication_confirmation_path redirect_to new_settings_two_factor_authentication_confirmation_path
end end

View file

@ -7,7 +7,23 @@ module WellKnown
def show def show
@webfinger_template = "#{webfinger_url}?resource={uri}" @webfinger_template = "#{webfinger_url}?resource={uri}"
expires_in 3.days, public: true expires_in 3.days, public: true
respond_to do |format|
format.any do
render content_type: 'application/xrd+xml', formats: [:xml] render content_type: 'application/xrd+xml', formats: [:xml]
end end
format.json do
render json: {
links: [
{
rel: 'lrdd',
template: @webfinger_template,
},
],
}
end
end
end
end end
end end

View file

@ -35,4 +35,11 @@ module Admin::ActionLogsHelper
end end
end end
end end
def sorted_action_log_types
Admin::ActionLogFilter::ACTION_TYPE_MAP
.keys
.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }
.sort_by(&:first)
end
end end

View file

@ -18,6 +18,11 @@ module Admin::DashboardHelper
end end
end end
def date_range(range)
[l(range.first), l(range.last)]
.join(' - ')
end
def relevant_account_timestamp(account) def relevant_account_timestamp(account)
timestamp, exact = if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago timestamp, exact = if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago
[account.user_current_sign_in_at, true] [account.user_current_sign_in_at, true]
@ -25,6 +30,8 @@ module Admin::DashboardHelper
[account.user_current_sign_in_at, false] [account.user_current_sign_in_at, false]
elsif account.user_pending? elsif account.user_pending?
[account.user_created_at, true] [account.user_created_at, true]
elsif account.suspended_at.present? && account.local? && account.user.nil?
[account.suspended_at, true]
elsif account.last_status_at.present? elsif account.last_status_at.present?
[account.last_status_at, true] [account.last_status_at, true]
else else

View file

@ -1,12 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module ApplicationHelper module ApplicationHelper
DANGEROUS_SCOPES = %w(
read
write
follow
).freeze
RTL_LOCALES = %i( RTL_LOCALES = %i(
ar ar
ckb ckb
@ -95,8 +89,11 @@ module ApplicationHelper
Rails.env.production? ? site_title : "#{site_title} (Dev)" Rails.env.production? ? site_title : "#{site_title} (Dev)"
end end
def class_for_scope(scope) def label_for_scope(scope)
'scope-danger' if DANGEROUS_SCOPES.include?(scope.to_s) safe_join [
tag.samp(scope, class: { 'scope-danger' => SessionActivation::DEFAULT_SCOPES.include?(scope.to_s) }),
tag.span(t("doorkeeper.scopes.#{scope}"), class: :hint),
]
end end
def can?(action, record) def can?(action, record)
@ -244,6 +241,10 @@ module ApplicationHelper
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg')) full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
end end
def copyable_input(options = {})
tag.input(type: :text, maxlength: 999, spellcheck: false, readonly: true, **options)
end
# glitch-soc addition to handle the multiple flavors # glitch-soc addition to handle the multiple flavors
def preload_locale_pack def preload_locale_pack
supported_locales = Themes.instance.flavour(current_flavour)['locales'] supported_locales = Themes.instance.flavour(current_flavour)['locales']

View file

@ -1,7 +0,0 @@
# frozen_string_literal: true
module WebfingerHelper
def webfinger!(uri)
Webfinger.new(uri).perform
end
end

View file

@ -37,8 +37,7 @@ export const synchronouslySubmitMarkers = createAppAsyncThunk(
}); });
return; return;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if ('sendBeacon' in navigator) {
} else if ('navigator' && 'sendBeacon' in navigator) {
// Failing that, we can use sendBeacon, but we have to encode the data as // Failing that, we can use sendBeacon, but we have to encode the data as
// FormData for DoorKeeper to recognize the token. // FormData for DoorKeeper to recognize the token.
const formData = new FormData(); const formData = new FormData();

View file

@ -70,6 +70,10 @@ function dispatchAssociatedRecords(
const supportedGroupedNotificationTypes = ['favourite', 'reblog']; const supportedGroupedNotificationTypes = ['favourite', 'reblog'];
export function shouldGroupNotificationType(type: string) {
return supportedGroupedNotificationTypes.includes(type);
}
export const fetchNotifications = createDataLoadingThunk( export const fetchNotifications = createDataLoadingThunk(
'notificationGroups/fetch', 'notificationGroups/fetch',
async (_params, { getState }) => async (_params, { getState }) =>

View file

@ -196,7 +196,7 @@ class Item extends PureComponent {
{visible && thumbnail} {visible && thumbnail}
{badges && ( {visible && badges && (
<div className='media-gallery__item__badges'> <div className='media-gallery__item__badges'>
{badges} {badges}
</div> </div>

View file

@ -402,7 +402,7 @@ export default function compose(state = initialState, action) {
.set('isUploadingThumbnail', false) .set('isUploadingThumbnail', false)
.update('media_attachments', list => list.map(item => { .update('media_attachments', list => list.map(item => {
if (item.get('id') === action.media.id) { if (item.get('id') === action.media.id) {
return fromJS(action.media); return fromJS(action.media).set('unattached', item.get('unattached'));
} }
return item; return item;

View file

@ -21,6 +21,7 @@ import {
unmountNotifications, unmountNotifications,
refreshStaleNotificationGroups, refreshStaleNotificationGroups,
pollRecentNotifications, pollRecentNotifications,
shouldGroupNotificationType,
} from 'mastodon/actions/notification_groups'; } from 'mastodon/actions/notification_groups';
import { import {
disconnectTimeline, disconnectTimeline,
@ -205,6 +206,13 @@ function processNewNotification(
groups: NotificationGroupsState['groups'], groups: NotificationGroupsState['groups'],
notification: ApiNotificationJSON, notification: ApiNotificationJSON,
) { ) {
if (!shouldGroupNotificationType(notification.type)) {
notification = {
...notification,
group_key: `ungrouped-${notification.id}`,
};
}
const existingGroupIndex = groups.findIndex( const existingGroupIndex = groups.findIndex(
(group) => (group) =>
group.type !== 'gap' && group.group_key === notification.group_key, group.type !== 'gap' && group.group_key === notification.group_key,
@ -242,7 +250,7 @@ function processNewNotification(
groups.unshift(existingGroup); groups.unshift(existingGroup);
} }
} else { } else {
// Create a new group // We have not found an existing group, create a new one
groups.unshift(createNotificationGroupFromNotificationJSON(notification)); groups.unshift(createNotificationGroupFromNotificationJSON(notification));
} }
} }

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-280q17 0 28.5-11.5T320-320q0-17-11.5-28.5T280-360q-17 0-28.5 11.5T240-320q0 17 11.5 28.5T280-280Zm-40-160h80v-240h-80v240Zm200 160h280v-80H440v80Zm0-160h280v-80H440v80Zm0-160h280v-80H440v80ZM160-120q-33 0-56.5-23.5T80-200v-560q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v560q0 33-23.5 56.5T800-120H160Z"/></svg>

After

Width:  |  Height:  |  Size: 419 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M280-280q17 0 28.5-11.5T320-320q0-17-11.5-28.5T280-360q-17 0-28.5 11.5T240-320q0 17 11.5 28.5T280-280Zm-40-160h80v-240h-80v240Zm200 160h280v-80H440v80Zm0-160h280v-80H440v80Zm0-160h280v-80H440v80ZM160-120q-33 0-56.5-23.5T80-200v-560q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v560q0 33-23.5 56.5T800-120H160Zm0-80h640v-560H160v560Zm0 0v-560 560Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-280q17 0 28.5-11.5T320-320q0-17-11.5-28.5T280-360q-17 0-28.5 11.5T240-320q0 17 11.5 28.5T280-280Zm-40-160h80v-240h-80v240Zm200 160h280v-80H440v80Zm0-160h280v-80H440v80Zm0-160h280v-80H440v80ZM160-120q-33 0-56.5-23.5T80-200v-560q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v560q0 33-23.5 56.5T800-120H160Zm0-80h640v-560H160v560Zm0 0v-560 560Z"/></svg>

Before

Width:  |  Height:  |  Size: 475 B

After

Width:  |  Height:  |  Size: 456 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M838-65 720-183v89h-80v-226h226v80h-90l118 118-56 57ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 20-2 40t-6 40h-82q5-20 7.5-40t2.5-40q0-20-2.5-40t-7.5-40H654q3 20 4.5 40t1.5 40q0 20-1.5 40t-4.5 40h-80q3-20 4.5-40t1.5-40q0-20-1.5-40t-4.5-40H386q-3 20-4.5 40t-1.5 40q0 20 1.5 40t4.5 40h134v80H404q12 43 31 82.5t45 75.5q20 0 40-2.5t40-4.5v82q-20 2-40 4.5T480-80ZM170-400h136q-3-20-4.5-40t-1.5-40q0-20 1.5-40t4.5-40H170q-5 20-7.5 40t-2.5 40q0 20 2.5 40t7.5 40Zm34-240h118q9-37 22.5-72.5T376-782q-55 18-99 54.5T204-640Zm172 462q-18-34-31.5-69.5T322-320H204q29 51 73 87.5t99 54.5Zm28-462h152q-12-43-31-82.5T480-798q-26 36-45 75.5T404-640Zm234 0h118q-29-51-73-87.5T584-782q18 34 31.5 69.5T638-640Z"/></svg>

After

Width:  |  Height:  |  Size: 898 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M838-65 720-183v89h-80v-226h226v80h-90l118 118-56 57ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 20-2 40t-6 40h-82q5-20 7.5-40t2.5-40q0-20-2.5-40t-7.5-40H654q3 20 4.5 40t1.5 40q0 20-1.5 40t-4.5 40h-80q3-20 4.5-40t1.5-40q0-20-1.5-40t-4.5-40H386q-3 20-4.5 40t-1.5 40q0 20 1.5 40t4.5 40h134v80H404q12 43 31 82.5t45 75.5q20 0 40-2.5t40-4.5v82q-20 2-40 4.5T480-80ZM170-400h136q-3-20-4.5-40t-1.5-40q0-20 1.5-40t4.5-40H170q-5 20-7.5 40t-2.5 40q0 20 2.5 40t7.5 40Zm34-240h118q9-37 22.5-72.5T376-782q-55 18-99 54.5T204-640Zm172 462q-18-34-31.5-69.5T322-320H204q29 51 73 87.5t99 54.5Zm28-462h152q-12-43-31-82.5T480-798q-26 36-45 75.5T404-640Zm234 0h118q-29-51-73-87.5T584-782q18 34 31.5 69.5T638-640Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M838-65 720-183v89h-80v-226h226v80h-90l118 118-56 57ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 20-2 40t-6 40h-82q5-20 7.5-40t2.5-40q0-20-2.5-40t-7.5-40H654q3 20 4.5 40t1.5 40q0 20-1.5 40t-4.5 40h-80q3-20 4.5-40t1.5-40q0-20-1.5-40t-4.5-40H386q-3 20-4.5 40t-1.5 40q0 20 1.5 40t4.5 40h134v80H404q12 43 31 82.5t45 75.5q20 0 40-2.5t40-4.5v82q-20 2-40 4.5T480-80ZM170-400h136q-3-20-4.5-40t-1.5-40q0-20 1.5-40t4.5-40H170q-5 20-7.5 40t-2.5 40q0 20 2.5 40t7.5 40Zm34-240h118q9-37 22.5-72.5T376-782q-55 18-99 54.5T204-640Zm172 462q-18-34-31.5-69.5T322-320H204q29 51 73 87.5t99 54.5Zm28-462h152q-12-43-31-82.5T480-798q-26 36-45 75.5T404-640Zm234 0h118q-29-51-73-87.5T584-782q18 34 31.5 69.5T638-640Z"/></svg>

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 898 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M80-80v-720q0-33 23.5-56.5T160-880h640q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H240L80-80Z"/></svg>

After

Width:  |  Height:  |  Size: 205 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M80-80v-720q0-33 23.5-56.5T160-880h640q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H240L80-80Zm126-240h594v-480H160v525l46-45Zm-46 0v-480 480Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M80-80v-720q0-33 23.5-56.5T160-880h640q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H240L80-80Zm126-240h594v-480H160v525l46-45Zm-46 0v-480 480Z"/></svg>

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 253 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Z"/></svg>

After

Width:  |  Height:  |  Size: 254 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Zm0-80h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41Zm220-240Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Zm0-80h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41Zm220-240Z"/></svg>

Before

Width:  |  Height:  |  Size: 424 B

After

Width:  |  Height:  |  Size: 405 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q23-81 85.5-136T440-797v323l-64-62-56 56 160 160 160-160-56-56-64 62v-323q103 14 171.5 92.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Z"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q17-72 85-137t145-65q33 0 56.5 23.5T520-716v242l64-62 56 56-160 160-160-160 56-56 64 62v-242q-76 14-118 73.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-48-22-89.5T600-680v-93q74 35 117 103.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Zm220-358Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q17-72 85-137t145-65q33 0 56.5 23.5T520-716v242l64-62 56 56-160 160-160-160 56-56 64 62v-242q-76 14-118 73.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-48-22-89.5T600-680v-93q74 35 117 103.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Zm220-358Z"/></svg>

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 469 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-160v-80h109q-51-44-80-106t-29-134q0-112 68-197.5T400-790v84q-70 25-115 86.5T240-480q0 54 21.5 99.5T320-302v-98h80v240H160Zm440 0q-50 0-85-35t-35-85q0-48 33-82.5t81-36.5q17-36 50.5-58.5T720-480q53 0 91.5 34.5T858-360q42 0 72 29t30 70q0 42-29 71.5T860-160H600Zm116-360q-7-41-27-76t-49-62v98h-80v-240h240v80H691q43 38 70.5 89T797-520h-81Z"/></svg>

After

Width:  |  Height:  |  Size: 446 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M160-160v-80h109q-51-44-80-106t-29-134q0-112 68-197.5T400-790v84q-70 25-115 86.5T240-480q0 54 21.5 99.5T320-302v-98h80v240H160Zm440 0q-50 0-85-35t-35-85q0-48 33-82.5t81-36.5q17-36 50.5-58.5T720-480q53 0 91.5 34.5T858-360q42 0 72 29t30 70q0 42-29 71.5T860-160H600Zm116-360q-7-41-27-76t-49-62v98h-80v-240h240v80H691q43 38 70.5 89T797-520h-81ZM600-240h260q8 0 14-6t6-14q0-8-6-14t-14-6h-70v-50q0-29-20.5-49.5T720-400q-29 0-49.5 20.5T650-330v10h-50q-17 0-28.5 11.5T560-280q0 17 11.5 28.5T600-240Zm120-80Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-160v-80h109q-51-44-80-106t-29-134q0-112 68-197.5T400-790v84q-70 25-115 86.5T240-480q0 54 21.5 99.5T320-302v-98h80v240H160Zm440 0q-50 0-85-35t-35-85q0-48 33-82.5t81-36.5q17-36 50.5-58.5T720-480q53 0 91.5 34.5T858-360q42 0 72 29t30 70q0 42-29 71.5T860-160H600Zm116-360q-7-41-27-76t-49-62v98h-80v-240h240v80H691q43 38 70.5 89T797-520h-81ZM600-240h260q8 0 14-6t6-14q0-8-6-14t-14-6h-70v-50q0-29-20.5-49.5T720-400q-29 0-49.5 20.5T650-330v10h-50q-17 0-28.5 11.5T560-280q0 17 11.5 28.5T600-240Zm120-80Z"/></svg>

Before

Width:  |  Height:  |  Size: 624 B

After

Width:  |  Height:  |  Size: 605 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-160H260q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H520v-286l64 62 56-56-160-160-160 160 56 56 64-62v286Z"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H520q-33 0-56.5-23.5T440-240v-206l-64 62-56-56 160-160 160 160-56 56-64-62v206h220q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h100v80H260Zm220-280Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H520q-33 0-56.5-23.5T440-240v-206l-64 62-56-56 160-160 160 160-56 56-64-62v206h220q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h100v80H260Zm220-280Z"/></svg>

Before

Width:  |  Height:  |  Size: 503 B

After

Width:  |  Height:  |  Size: 484 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M320-240 80-480l240-240 57 57-184 184 183 183-56 56Zm320 0-57-57 184-184-183-183 56-56 240 240-240 240Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-240 80-480l240-240 57 57-184 184 183 183-56 56Zm320 0-57-57 184-184-183-183 56-56 240 240-240 240Z"/></svg>

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 209 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-240q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160ZM40-120v-80h880v80H40Z"/></svg>

After

Width:  |  Height:  |  Size: 246 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/></svg>

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 283 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M560-520h280v-200H560v200Zm140-50-100-70v-40l100 70 100-70v40l-100 70ZM80-120q-33 0-56.5-23.5T0-200v-560q0-33 23.5-56.5T80-840h800q33 0 56.5 23.5T960-760v560q0 33-23.5 56.5T880-120H80Zm280-280q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM84-200h552q-42-75-116-117.5T360-360q-86 0-160 42.5T84-200Z"/></svg>

After

Width:  |  Height:  |  Size: 429 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M560-520h280v-200H560v200Zm140-50-100-70v-40l100 70 100-70v40l-100 70ZM80-120q-33 0-56.5-23.5T0-200v-560q0-33 23.5-56.5T80-840h800q33 0 56.5 23.5T960-760v560q0 33-23.5 56.5T880-120H80Zm556-80h244v-560H80v560h4q42-75 116-117.5T360-360q86 0 160 42.5T636-200ZM360-400q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM182-200h356q-34-38-80.5-59T360-280q-51 0-97 21t-81 59Zm178-280q-17 0-28.5-11.5T320-520q0-17 11.5-28.5T360-560q17 0 28.5 11.5T400-520q0 17-11.5 28.5T360-480Zm120 0Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M560-520h280v-200H560v200Zm140-50-100-70v-40l100 70 100-70v40l-100 70ZM80-120q-33 0-56.5-23.5T0-200v-560q0-33 23.5-56.5T80-840h800q33 0 56.5 23.5T960-760v560q0 33-23.5 56.5T880-120H80Zm556-80h244v-560H80v560h4q42-75 116-117.5T360-360q86 0 160 42.5T636-200ZM360-400q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM182-200h356q-34-38-80.5-59T360-280q-51 0-97 21t-81 59Zm178-280q-17 0-28.5-11.5T320-520q0-17 11.5-28.5T360-560q17 0 28.5 11.5T400-520q0 17-11.5 28.5T360-480Zm120 0Z"/></svg>

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 606 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-520q150 0 255-47t105-113q0-66-105-113t-255-47q-150 0-255 47T120-680q0 66 105 113t255 47Zm0 100q41 0 102.5-8.5T701-456q57-19 98-49.5t41-74.5v100q0 44-41 74.5T701-356q-57 19-118.5 27.5T480-320q-41 0-102.5-8.5T259-356q-57-19-98-49.5T120-480v-100q0 44 41 74.5t98 49.5q57 19 118.5 27.5T480-420Zm0 200q41 0 102.5-8.5T701-256q57-19 98-49.5t41-74.5v100q0 44-41 74.5T701-156q-57 19-118.5 27.5T480-120q-41 0-102.5-8.5T259-156q-57-19-98-49.5T120-280v-100q0 44 41 74.5t98 49.5q57 19 118.5 27.5T480-220Z"/></svg>

After

Width:  |  Height:  |  Size: 601 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M480-120q-151 0-255.5-46.5T120-280v-400q0-66 105.5-113T480-840q149 0 254.5 47T840-680v400q0 67-104.5 113.5T480-120Zm0-479q89 0 179-25.5T760-679q-11-29-100.5-55T480-760q-91 0-178.5 25.5T200-679q14 30 101.5 55T480-599Zm0 199q42 0 81-4t74.5-11.5q35.5-7.5 67-18.5t57.5-25v-120q-26 14-57.5 25t-67 18.5Q600-528 561-524t-81 4q-42 0-82-4t-75.5-11.5Q287-543 256-554t-56-25v120q25 14 56 25t66.5 18.5Q358-408 398-404t82 4Zm0 200q46 0 93.5-7t87.5-18.5q40-11.5 67-26t32-29.5v-98q-26 14-57.5 25t-67 18.5Q600-328 561-324t-81 4q-42 0-82-4t-75.5-11.5Q287-343 256-354t-56-25v99q5 15 31.5 29t66.5 25.5q40 11.5 88 18.5t94 7Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-120q-151 0-255.5-46.5T120-280v-400q0-66 105.5-113T480-840q149 0 254.5 47T840-680v400q0 67-104.5 113.5T480-120Zm0-479q89 0 179-25.5T760-679q-11-29-100.5-55T480-760q-91 0-178.5 25.5T200-679q14 30 101.5 55T480-599Zm0 199q42 0 81-4t74.5-11.5q35.5-7.5 67-18.5t57.5-25v-120q-26 14-57.5 25t-67 18.5Q600-528 561-524t-81 4q-42 0-82-4t-75.5-11.5Q287-543 256-554t-56-25v120q25 14 56 25t66.5 18.5Q358-408 398-404t82 4Zm0 200q46 0 93.5-7t87.5-18.5q40-11.5 67-26t32-29.5v-98q-26 14-57.5 25t-67 18.5Q600-328 561-324t-81 4q-42 0-82-4t-75.5-11.5Q287-343 256-354t-56-25v99q5 15 31.5 29t66.5 25.5q40 11.5 88 18.5t94 7Z"/></svg>

Before

Width:  |  Height:  |  Size: 729 B

After

Width:  |  Height:  |  Size: 710 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m368-630 106-210h12l106 210H368Zm82 474L105-570h345v414Zm60 0v-414h345L510-156Zm148-474L554-840h206l105 210H658Zm-563 0 105-210h206L302-630H95Z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M480-120 80-600l120-240h560l120 240-400 480Zm-95-520h190l-60-120h-70l-60 120Zm55 347v-267H218l222 267Zm80 0 222-267H520v267Zm144-347h106l-60-120H604l60 120Zm-474 0h106l60-120H250l-60 120Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-120 80-600l120-240h560l120 240-400 480Zm-95-520h190l-60-120h-70l-60 120Zm55 347v-267H218l222 267Zm80 0 222-267H520v267Zm144-347h106l-60-120H604l60 120Zm-474 0h106l60-120H250l-60 120Z"/></svg>

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 293 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Z"/></svg>

After

Width:  |  Height:  |  Size: 238 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-308 198-252H282l198 252Zm0 0Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-308 198-252H282l198 252Zm0 0Z"/></svg>

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 271 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Z"/></svg>

After

Width:  |  Height:  |  Size: 639 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780Zm-455-80h311q-10-20-55.5-35T480-370q-55 0-100.5 15T325-320ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm0-80q17 0 28.5-11.5T520-600q0-17-11.5-28.5T480-640q-17 0-28.5 11.5T440-600q0 17 11.5 28.5T480-560Zm1 240Zm-1-280Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780Zm-455-80h311q-10-20-55.5-35T480-370q-55 0-100.5 15T325-320ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm0-80q17 0 28.5-11.5T520-600q0-17-11.5-28.5T480-640q-17 0-28.5 11.5T440-600q0 17 11.5 28.5T480-560Zm1 240Zm-1-280Z"/></svg>

Before

Width:  |  Height:  |  Size: 831 B

After

Width:  |  Height:  |  Size: 812 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m791-55-91-91q-49 32-104.5 49T480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-60 17-115.5T146-700l-91-91 57-57 736 736-57 57Zm23-205L260-814q49-32 104.5-49T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 60-17 115.5T814-260Z"/></svg>

After

Width:  |  Height:  |  Size: 344 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="m791-55-91-91q-49 32-104.5 49T480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-60 17-115.5T146-700l-91-91 57-57 736 736-57 57ZM480-160q43 0 83.5-11t78.5-33L204-642q-22 38-33 78.5T160-480q0 133 93.5 226.5T480-160Zm334-100-58-58q22-38 33-78.5t11-83.5q0-133-93.5-226.5T480-800q-43 0-83.5 11T318-756l-58-58q49-32 104.5-49T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 60-17 115.5T814-260ZM537-537ZM423-423Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m791-55-91-91q-49 32-104.5 49T480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-60 17-115.5T146-700l-91-91 57-57 736 736-57 57ZM480-160q43 0 83.5-11t78.5-33L204-642q-22 38-33 78.5T160-480q0 133 93.5 226.5T480-160Zm334-100-58-58q22-38 33-78.5t11-83.5q0-133-93.5-226.5T480-800q-43 0-83.5 11T318-756l-58-58q49-32 104.5-49T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 60-17 115.5T814-260ZM537-537ZM423-423Z"/></svg>

Before

Width:  |  Height:  |  Size: 542 B

After

Width:  |  Height:  |  Size: 523 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm280-200q38 0 69-22t43-58h168v-360H200v360h168q12 36 43 58t69 22Z"/></svg>

After

Width:  |  Height:  |  Size: 290 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-120H640q-30 38-71.5 59T480-240q-47 0-88.5-21T320-320H200v120Zm280-120q38 0 69-22t43-58h168v-360H200v360h168q12 36 43 58t69 22ZM200-200h560-560Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-120H640q-30 38-71.5 59T480-240q-47 0-88.5-21T320-320H200v120Zm280-120q38 0 69-22t43-58h168v-360H200v360h168q12 36 43 58t69 22ZM200-200h560-560Z"/></svg>

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 379 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-600v-80h560v80H280Zm0 160v-80h560v80H280Zm0 160v-80h560v80H280ZM160-600q-17 0-28.5-11.5T120-640q0-17 11.5-28.5T160-680q17 0 28.5 11.5T200-640q0 17-11.5 28.5T160-600Zm0 160q-17 0-28.5-11.5T120-480q0-17 11.5-28.5T160-520q17 0 28.5 11.5T200-480q0 17-11.5 28.5T160-440Zm0 160q-17 0-28.5-11.5T120-320q0-17 11.5-28.5T160-360q17 0 28.5 11.5T200-320q0 17-11.5 28.5T160-280Z"/></svg>

After

Width:  |  Height:  |  Size: 476 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M280-600v-80h560v80H280Zm0 160v-80h560v80H280Zm0 160v-80h560v80H280ZM160-600q-17 0-28.5-11.5T120-640q0-17 11.5-28.5T160-680q17 0 28.5 11.5T200-640q0 17-11.5 28.5T160-600Zm0 160q-17 0-28.5-11.5T120-480q0-17 11.5-28.5T160-520q17 0 28.5 11.5T200-480q0 17-11.5 28.5T160-440Zm0 160q-17 0-28.5-11.5T120-320q0-17 11.5-28.5T160-360q17 0 28.5 11.5T200-320q0 17-11.5 28.5T160-280Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-600v-80h560v80H280Zm0 160v-80h560v80H280Zm0 160v-80h560v80H280ZM160-600q-17 0-28.5-11.5T120-640q0-17 11.5-28.5T160-680q17 0 28.5 11.5T200-640q0 17-11.5 28.5T160-600Zm0 160q-17 0-28.5-11.5T120-480q0-17 11.5-28.5T160-520q17 0 28.5 11.5T200-480q0 17-11.5 28.5T160-440Zm0 160q-17 0-28.5-11.5T120-320q0-17 11.5-28.5T160-360q17 0 28.5 11.5T200-320q0 17-11.5 28.5T160-280Z"/></svg>

Before

Width:  |  Height:  |  Size: 495 B

After

Width:  |  Height:  |  Size: 476 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/></svg>

Before

Width:  |  Height:  |  Size: 328 B

After

Width:  |  Height:  |  Size: 309 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M620-520q25 0 42.5-17.5T680-580q0-25-17.5-42.5T620-640q-25 0-42.5 17.5T560-580q0 25 17.5 42.5T620-520Zm-280 0q25 0 42.5-17.5T400-580q0-25-17.5-42.5T340-640q-25 0-42.5 17.5T280-580q0 25 17.5 42.5T340-520Zm140 260q68 0 123.5-38.5T684-400H276q25 63 80.5 101.5T480-260Zm0 180q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z"/></svg>

After

Width:  |  Height:  |  Size: 559 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M620-520q25 0 42.5-17.5T680-580q0-25-17.5-42.5T620-640q-25 0-42.5 17.5T560-580q0 25 17.5 42.5T620-520Zm-280 0q25 0 42.5-17.5T400-580q0-25-17.5-42.5T340-640q-25 0-42.5 17.5T280-580q0 25 17.5 42.5T340-520Zm140 260q68 0 123.5-38.5T684-400H276q25 63 80.5 101.5T480-260Zm0 180q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-400Zm0 320q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M620-520q25 0 42.5-17.5T680-580q0-25-17.5-42.5T620-640q-25 0-42.5 17.5T560-580q0 25 17.5 42.5T620-520Zm-280 0q25 0 42.5-17.5T400-580q0-25-17.5-42.5T340-640q-25 0-42.5 17.5T280-580q0 25 17.5 42.5T340-520Zm140 260q68 0 123.5-38.5T684-400H276q25 63 80.5 101.5T480-260Zm0 180q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-400Zm0 320q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 656 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240ZM330-120 120-330v-300l210-210h300l210 210v300L630-120H330Z"/></svg>

After

Width:  |  Height:  |  Size: 291 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240ZM330-120 120-330v-300l210-210h300l210 210v300L630-120H330Zm34-80h232l164-164v-232L596-760H364L200-596v232l164 164Zm116-280Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240ZM330-120 120-330v-300l210-210h300l210 210v300L630-120H330Zm34-80h232l164-164v-232L596-760H364L200-596v232l164 164Zm116-280Z"/></svg>

Before

Width:  |  Height:  |  Size: 375 B

After

Width:  |  Height:  |  Size: 356 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-200q83 0 141.5-58.5T680-480q0-83-58.5-141.5T480-680q-83 0-141.5 58.5T280-480q0 83 58.5 141.5T480-280Zm66-106-86-86v-128h40v112l74 74-28 28Z"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M480-280q83 0 141.5-58.5T680-480q0-83-58.5-141.5T480-680q-83 0-141.5 58.5T280-480q0 83 58.5 141.5T480-280Zm66-106-86-86v-128h40v112l74 74-28 28ZM480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-84q104-33 172-132t68-220v-189l-240-90-240 90v189q0 121 68 220t172 132Zm0-316Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-280q83 0 141.5-58.5T680-480q0-83-58.5-141.5T480-680q-83 0-141.5 58.5T280-480q0 83 58.5 141.5T480-280Zm66-106-86-86v-128h40v112l74 74-28 28ZM480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-84q104-33 172-132t68-220v-189l-240-90-240 90v189q0 121 68 220t172 132Zm0-316Z"/></svg>

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 415 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M418-340q25 25 63 23.5t55-27.5l224-336-336 224q-26 18-28.5 54.5T418-340ZM204-160q-22 0-40.5-9.5T134-198q-26-47-40-97.5T80-400q0-83 31.5-156T197-683q54-54 127-85.5T480-800q82 0 154 31t126 84.5q54 53.5 86 125T879-406q1 55-12.5 107.5T825-198q-11 19-29.5 28.5T755-160H204Z"/></svg>

After

Width:  |  Height:  |  Size: 374 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M418-340q24 24 62 23.5t56-27.5l224-336-336 224q-27 18-28.5 55t22.5 61Zm62-460q59 0 113.5 16.5T696-734l-76 48q-33-17-68.5-25.5T480-720q-133 0-226.5 93.5T160-400q0 42 11.5 83t32.5 77h552q23-38 33.5-79t10.5-85q0-36-8.5-70T766-540l48-76q30 47 47.5 100T880-406q1 57-13 109t-41 99q-11 18-30 28t-40 10H204q-21 0-40-10t-30-28q-26-45-40-95.5T80-400q0-83 31.5-155.5t86-127Q252-737 325-768.5T480-800Zm7 313Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M418-340q24 24 62 23.5t56-27.5l224-336-336 224q-27 18-28.5 55t22.5 61Zm62-460q59 0 113.5 16.5T696-734l-76 48q-33-17-68.5-25.5T480-720q-133 0-226.5 93.5T160-400q0 42 11.5 83t32.5 77h552q23-38 33.5-79t10.5-85q0-36-8.5-70T766-540l48-76q30 47 47.5 100T880-406q1 57-13 109t-41 99q-11 18-30 28t-40 10H204q-21 0-40-10t-30-28q-26-45-40-95.5T80-400q0-83 31.5-155.5t86-127Q252-737 325-768.5T480-800Zm7 313Z"/></svg>

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 502 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m136-240-56-56 296-298 160 160 208-206H640v-80h240v240h-80v-104L536-320 376-480 136-240Z"/></svg>

After

Width:  |  Height:  |  Size: 194 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="m136-240-56-56 296-298 160 160 208-206H640v-80h240v240h-80v-104L536-320 376-480 136-240Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m136-240-56-56 296-298 160 160 208-206H640v-80h240v240h-80v-104L536-320 376-480 136-240Z"/></svg>

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 194 B

View file

@ -226,6 +226,10 @@ $content-width: 840px;
gap: 5px; gap: 5px;
white-space: nowrap; white-space: nowrap;
@media screen and (max-width: $mobile-breakpoint) {
flex: 1 0 50%;
}
&:hover, &:hover,
&:focus, &:focus,
&:active { &:active {
@ -1070,6 +1074,10 @@ a.name-tag,
} }
} }
.icon {
vertical-align: middle;
}
a.announcements-list__item__title { a.announcements-list__item__title {
&:hover, &:hover,
&:focus, &:focus,

View file

@ -3611,6 +3611,7 @@ $ui-header-logo-wordmark-width: 99px;
overflow-y: auto; overflow-y: auto;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 0;
} }
.drawer__inner__mastodon { .drawer__inner__mastodon {

View file

@ -137,6 +137,7 @@ a.table-action-link {
padding: 0 10px; padding: 0 10px;
color: $darker-text-color; color: $darker-text-color;
font-weight: 500; font-weight: 500;
white-space: nowrap;
&:hover { &:hover {
color: $highlight-text-color; color: $highlight-text-color;

View file

@ -42,6 +42,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def process_status def process_status
@tags = [] @tags = []
@mentions = [] @mentions = []
@unresolved_mentions = []
@silenced_account_ids = [] @silenced_account_ids = []
@params = {} @params = {}
@ -55,6 +56,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end end
resolve_thread(@status) resolve_thread(@status)
resolve_unresolved_mentions(@status)
fetch_replies(@status) fetch_replies(@status)
distribute distribute
forward_for_reply forward_for_reply
@ -197,6 +199,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
return if account.nil? return if account.nil?
@mentions << Mention.new(account: account, silent: false) @mentions << Mention.new(account: account, silent: false)
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError
@unresolved_mentions << tag['href']
end end
def process_emoji(tag) def process_emoji(tag)
@ -301,6 +305,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
ThreadResolveWorker.perform_async(status.id, in_reply_to_uri, { 'request_id' => @options[:request_id] }) ThreadResolveWorker.perform_async(status.id, in_reply_to_uri, { 'request_id' => @options[:request_id] })
end end
def resolve_unresolved_mentions(status)
@unresolved_mentions.uniq.each do |uri|
MentionResolveWorker.perform_in(rand(30...600).seconds, status.id, uri, { 'request_id' => @options[:request_id] })
end
end
def fetch_replies(status) def fetch_replies(status)
collection = @object['replies'] collection = @object['replies']
return if collection.blank? return if collection.blank?

View file

@ -9,10 +9,10 @@ class Vacuum::ImportsVacuum
private private
def clean_unconfirmed_imports! def clean_unconfirmed_imports!
BulkImport.state_unconfirmed.where(created_at: ..10.minutes.ago).reorder(nil).in_batches.delete_all BulkImport.state_unconfirmed.where(created_at: ..10.minutes.ago).in_batches.delete_all
end end
def clean_old_imports! def clean_old_imports!
BulkImport.where(created_at: ..1.week.ago).reorder(nil).in_batches.delete_all BulkImport.where(created_at: ..1.week.ago).in_batches.delete_all
end end
end end

View file

@ -0,0 +1,72 @@
# frozen_string_literal: true
class WebPushRequest
SIGNATURE_ALGORITHM = 'p256ecdsa'
AUTH_HEADER = 'WebPush'
PAYLOAD_EXPIRATION = 24.hours
JWT_ALGORITHM = 'ES256'
JWT_TYPE = 'JWT'
attr_reader :web_push_subscription
delegate(
:endpoint,
:key_auth,
:key_p256dh,
to: :web_push_subscription
)
def initialize(web_push_subscription)
@web_push_subscription = web_push_subscription
end
def audience
@audience ||= Addressable::URI.parse(endpoint).normalized_site
end
def authorization_header
[AUTH_HEADER, encoded_json_web_token].join(' ')
end
def crypto_key_header
[SIGNATURE_ALGORITHM, vapid_key.public_key_for_push_header].join('=')
end
def encrypt(payload)
Webpush::Encryption.encrypt(payload, key_p256dh, key_auth)
end
private
def encoded_json_web_token
JWT.encode(
web_token_payload,
vapid_key.curve,
JWT_ALGORITHM,
typ: JWT_TYPE
)
end
def web_token_payload
{
aud: audience,
exp: PAYLOAD_EXPIRATION.from_now.to_i,
sub: payload_subject,
}
end
def payload_subject
[:mailto, contact_email].join(':')
end
def vapid_key
@vapid_key ||= Webpush::VapidKey.from_keys(
Rails.configuration.x.vapid_public_key,
Rails.configuration.x.vapid_private_key
)
end
def contact_email
@contact_email ||= ::Setting.site_contact_email
end
end

View file

@ -21,7 +21,7 @@ class AccountFilter
end end
def results def results
scope = Account.includes(:account_stat, user: [:ips, :invite_request]).without_instance_actor.reorder(nil) scope = Account.includes(:account_stat, user: [:ips, :invite_request]).without_instance_actor
relevant_params.each do |key, value| relevant_params.each do |key, value|
next if key.to_s == 'page' next if key.to_s == 'page'

View file

@ -14,7 +14,7 @@ class Admin::TagFilter
end end
def results def results
scope = Tag.reorder(nil) scope = Tag.all
params.each do |key, value| params.each do |key, value|
next if key == :page next if key == :page

View file

@ -6,10 +6,13 @@ module Account::Avatar
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
LIMIT = 2.megabytes LIMIT = 2.megabytes
AVATAR_DIMENSIONS = [400, 400].freeze
AVATAR_GEOMETRY = [AVATAR_DIMENSIONS.first, AVATAR_DIMENSIONS.last].join('x')
class_methods do class_methods do
def avatar_styles(file) def avatar_styles(file)
styles = { original: { geometry: '400x400#', file_geometry_parser: FastGeometryParser } } styles = { original: { geometry: "#{AVATAR_GEOMETRY}#", file_geometry_parser: FastGeometryParser } }
styles[:static] = { geometry: '400x400#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif' styles[:static] = { geometry: "#{AVATAR_GEOMETRY}#", format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
styles styles
end end

View file

@ -5,7 +5,10 @@ module Account::Header
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
LIMIT = 2.megabytes LIMIT = 2.megabytes
MAX_PIXELS = 750_000 # 1500x500px
HEADER_DIMENSIONS = [1500, 500].freeze
HEADER_GEOMETRY = [HEADER_DIMENSIONS.first, HEADER_DIMENSIONS.last].join('x')
MAX_PIXELS = HEADER_DIMENSIONS.first * HEADER_DIMENSIONS.last
class_methods do class_methods do
def header_styles(file) def header_styles(file)

View file

@ -3,7 +3,6 @@
class RemoteFollow class RemoteFollow
include ActiveModel::Validations include ActiveModel::Validations
include RoutingHelper include RoutingHelper
include WebfingerHelper
attr_accessor :acct, :addressable_template attr_accessor :acct, :addressable_template
@ -66,7 +65,7 @@ class RemoteFollow
end end
def acct_resource def acct_resource
@acct_resource ||= webfinger!("acct:#{acct}") @acct_resource ||= Webfinger.new("acct:#{acct}").perform
rescue Webfinger::Error, HTTP::ConnectionError rescue Webfinger::Error, HTTP::ConnectionError
nil nil
end end

View file

@ -18,13 +18,25 @@ class ReportFilter
def results def results
scope = Report.unresolved scope = Report.unresolved
params.each do |key, value| relevant_params.each do |key, value|
scope = scope.merge scope_for(key, value) scope = scope.merge scope_for(key, value)
end end
scope scope
end end
private
def relevant_params
params.tap do |args|
args.delete(:target_origin) if origin_is_remote_and_domain_present?
end
end
def origin_is_remote_and_domain_present?
params[:target_origin] == 'remote' && params[:by_target_domain].present?
end
def scope_for(key, value) def scope_for(key, value)
case key.to_sym case key.to_sym
when :by_target_domain when :by_target_domain

View file

@ -28,6 +28,8 @@ class SessionActivation < ApplicationRecord
before_create :assign_access_token before_create :assign_access_token
DEFAULT_SCOPES = %w(read write follow).freeze
class << self class << self
def active?(id) def active?(id)
id && exists?(session_id: id) id && exists?(session_id: id)
@ -64,7 +66,7 @@ class SessionActivation < ApplicationRecord
{ {
application_id: Doorkeeper::Application.find_by(superapp: true)&.id, application_id: Doorkeeper::Application.find_by(superapp: true)&.id,
resource_owner_id: user_id, resource_owner_id: user_id,
scopes: 'read write follow', scopes: DEFAULT_SCOPES.join(' '),
expires_in: Doorkeeper.configuration.access_token_expires_in, expires_in: Doorkeeper.configuration.access_token_expires_in,
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?, use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?,
} }

View file

@ -71,7 +71,8 @@ class User < ApplicationRecord
ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days.freeze ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days.freeze
devise :two_factor_authenticatable, devise :two_factor_authenticatable,
otp_secret_encryption_key: Rails.configuration.x.otp_secret otp_secret_encryption_key: Rails.configuration.x.otp_secret,
otp_secret_length: 32
include LegacyOtpSecret # Must be after the above `devise` line in order to override the legacy method include LegacyOtpSecret # Must be after the above `devise` line in order to override the legacy method

View file

@ -29,26 +29,6 @@ class Web::PushSubscription < ApplicationRecord
delegate :locale, to: :associated_user delegate :locale, to: :associated_user
def encrypt(payload)
Webpush::Encryption.encrypt(payload, key_p256dh, key_auth)
end
def audience
@audience ||= Addressable::URI.parse(endpoint).normalized_site
end
def crypto_key_header
p256ecdsa = vapid_key.public_key_for_push_header
"p256ecdsa=#{p256ecdsa}"
end
def authorization_header
jwt = JWT.encode({ aud: audience, exp: 24.hours.from_now.to_i, sub: "mailto:#{contact_email}" }, vapid_key.curve, 'ES256', typ: 'JWT')
"WebPush #{jwt}"
end
def pushable?(notification) def pushable?(notification)
policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification) policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification)
end end
@ -92,14 +72,6 @@ class Web::PushSubscription < ApplicationRecord
) )
end end
def vapid_key
@vapid_key ||= Webpush::VapidKey.from_keys(Rails.configuration.x.vapid_public_key, Rails.configuration.x.vapid_private_key)
end
def contact_email
@contact_email ||= ::Setting.site_contact_email
end
def alert_enabled_for_notification_type?(notification) def alert_enabled_for_notification_type?(notification)
truthy?(data&.dig('alerts', notification.type.to_s)) truthy?(data&.dig('alerts', notification.type.to_s))
end end

View file

@ -3,7 +3,6 @@
class ActivityPub::FetchRemoteActorService < BaseService class ActivityPub::FetchRemoteActorService < BaseService
include JsonLdHelper include JsonLdHelper
include DomainControlHelper include DomainControlHelper
include WebfingerHelper
class Error < StandardError; end class Error < StandardError; end
@ -45,7 +44,7 @@ class ActivityPub::FetchRemoteActorService < BaseService
private private
def check_webfinger! def check_webfinger!
webfinger = webfinger!("acct:#{@username}@#{@domain}") webfinger = Webfinger.new("acct:#{@username}@#{@domain}").perform
confirmed_username, confirmed_domain = split_acct(webfinger.subject) confirmed_username, confirmed_domain = split_acct(webfinger.subject)
if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero? if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero?
@ -54,7 +53,7 @@ class ActivityPub::FetchRemoteActorService < BaseService
return return
end end
webfinger = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}") webfinger = Webfinger.new("acct:#{confirmed_username}@#{confirmed_domain}").perform
@username, @domain = split_acct(webfinger.subject) @username, @domain = split_acct(webfinger.subject)
raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{@uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{@uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?

View file

@ -2,6 +2,7 @@
class ActivityPub::ProcessCollectionService < BaseService class ActivityPub::ProcessCollectionService < BaseService
include JsonLdHelper include JsonLdHelper
include DomainControlHelper
def call(body, actor, **options) def call(body, actor, **options)
@account = actor @account = actor
@ -69,6 +70,9 @@ class ActivityPub::ProcessCollectionService < BaseService
end end
def verify_account! def verify_account!
return unless @json['signature'].is_a?(Hash)
return if domain_not_allowed?(@json['signature']['creator'])
@options[:relayed_through_actor] = @account @options[:relayed_through_actor] = @account
@account = ActivityPub::LinkedDataSignature.new(@json).verify_actor! @account = ActivityPub::LinkedDataSignature.new(@json).verify_actor!
@account = nil unless @account.is_a?(Account) @account = nil unless @account.is_a?(Account)

View file

@ -16,12 +16,12 @@ class PurgeDomainService < BaseService
end end
def purge_accounts! def purge_accounts!
Account.remote.where(domain: @domain).reorder(nil).find_each do |account| Account.remote.where(domain: @domain).find_each do |account|
DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true)
end end
end end
def purge_emojis! def purge_emojis!
CustomEmoji.remote.where(domain: @domain).reorder(nil).find_each(&:destroy) CustomEmoji.remote.where(domain: @domain).find_each(&:destroy)
end end
end end

View file

@ -2,7 +2,6 @@
class ResolveAccountService < BaseService class ResolveAccountService < BaseService
include DomainControlHelper include DomainControlHelper
include WebfingerHelper
include Redisable include Redisable
include Lockable include Lockable
@ -81,7 +80,7 @@ class ResolveAccountService < BaseService
end end
def process_webfinger!(uri) def process_webfinger!(uri)
@webfinger = webfinger!("acct:#{uri}") @webfinger = Webfinger.new("acct:#{uri}").perform
confirmed_username, confirmed_domain = split_acct(@webfinger.subject) confirmed_username, confirmed_domain = split_acct(@webfinger.subject)
if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
@ -91,7 +90,7 @@ class ResolveAccountService < BaseService
end end
# Account doesn't match, so it may have been redirected # Account doesn't match, so it may have been redirected
@webfinger = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}") @webfinger = Webfinger.new("acct:#{confirmed_username}@#{confirmed_domain}").perform
@username, @domain = split_acct(@webfinger.subject) @username, @domain = split_acct(@webfinger.subject)
raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?

View file

@ -16,7 +16,7 @@
%strong= t('admin.action_logs.filter_by_action') %strong= t('admin.action_logs.filter_by_action')
.input.select.optional .input.select.optional
= form.select :action_type, = form.select :action_type,
options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }, params[:action_type]), options_for_select(sorted_action_log_types, params[:action_type]),
prompt: I18n.t('admin.accounts.moderation.all') prompt: I18n.t('admin.accounts.moderation.all')
- if @action_logs.empty? - if @action_logs.empty?

View file

@ -2,9 +2,7 @@
= t('admin.dashboard.title') = t('admin.dashboard.title')
- content_for :heading_actions do - content_for :heading_actions do
= l(@time_period.first) = date_range(@time_period)
= ' - '
= l(@time_period.last)
- unless @system_checks.empty? - unless @system_checks.empty?
.flash-message-stack .flash-message-stack

View file

@ -16,7 +16,7 @@
label: I18n.t('admin.email_domain_blocks.allow_registrations_with_approval'), label: I18n.t('admin.email_domain_blocks.allow_registrations_with_approval'),
wrapper: :with_label wrapper: :with_label
- if defined?(@resolved_records) - if defined?(@resolved_records) && @resolved_records.any?
%p.hint= t('admin.email_domain_blocks.resolved_dns_records_hint_html') %p.hint= t('admin.email_domain_blocks.resolved_dns_records_hint_html')
.batch-table .batch-table

View file

@ -0,0 +1,66 @@
-# locals: (instance_domain:, period_end_at:, period_start_at:)
%p
= material_symbol 'info'
= t('admin.instances.totals_time_period_hint_html')
.dashboard
.dashboard__item
= react_admin_component :counter,
end_at: period_end_at,
href: admin_accounts_path(origin: 'remote', by_domain: instance_domain),
label: t('admin.instances.dashboard.instance_accounts_measure'),
measure: 'instance_accounts',
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :counter,
end_at: period_end_at,
label: t('admin.instances.dashboard.instance_statuses_measure'),
measure: 'instance_statuses',
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :counter,
end_at: period_end_at,
label: t('admin.instances.dashboard.instance_media_attachments_measure'),
measure: 'instance_media_attachments',
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :counter,
end_at: period_end_at,
label: t('admin.instances.dashboard.instance_follows_measure'),
measure: 'instance_follows',
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :counter,
end_at: period_end_at,
label: t('admin.instances.dashboard.instance_followers_measure'),
measure: 'instance_followers',
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :counter,
end_at: period_end_at,
href: admin_reports_path(by_target_domain: instance_domain),
label: t('admin.instances.dashboard.instance_reports_measure'),
measure: 'instance_reports',
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :dimension,
dimension: 'instance_accounts',
end_at: period_end_at,
label: t('admin.instances.dashboard.instance_accounts_dimension'),
limit: 8,
params: { domain: instance_domain },
start_at: period_start_at
.dashboard__item
= react_admin_component :dimension,
dimension: 'instance_languages',
end_at: period_end_at,
label: t('admin.instances.dashboard.instance_languages_dimension'),
limit: 8,
params: { domain: instance_domain },
start_at: period_start_at

View file

@ -3,77 +3,10 @@
- if current_user.can?(:view_dashboard) - if current_user.can?(:view_dashboard)
- content_for :heading_actions do - content_for :heading_actions do
= l(@time_period.first) = date_range(@time_period)
= ' - '
= l(@time_period.last)
- if @instance.persisted? - if @instance.persisted?
%p = render 'dashboard', instance_domain: @instance.domain, period_end_at: @time_period.last, period_start_at: @time_period.first
= material_symbol 'info'
= t('admin.instances.totals_time_period_hint_html')
.dashboard
.dashboard__item
= react_admin_component :counter,
end_at: @time_period.last,
href: admin_accounts_path(origin: 'remote', by_domain: @instance.domain),
label: t('admin.instances.dashboard.instance_accounts_measure'),
measure: 'instance_accounts',
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :counter,
end_at: @time_period.last,
label: t('admin.instances.dashboard.instance_statuses_measure'),
measure: 'instance_statuses',
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :counter,
end_at: @time_period.last,
label: t('admin.instances.dashboard.instance_media_attachments_measure'),
measure: 'instance_media_attachments',
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :counter,
end_at: @time_period.last,
label: t('admin.instances.dashboard.instance_follows_measure'),
measure: 'instance_follows',
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :counter,
end_at: @time_period.last,
label: t('admin.instances.dashboard.instance_followers_measure'),
measure: 'instance_followers',
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :counter,
end_at: @time_period.last,
href: admin_reports_path(by_target_domain: @instance.domain),
label: t('admin.instances.dashboard.instance_reports_measure'),
measure: 'instance_reports',
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :dimension,
dimension: 'instance_accounts',
end_at: @time_period.last,
label: t('admin.instances.dashboard.instance_accounts_dimension'),
limit: 8,
params: { domain: @instance.domain },
start_at: @time_period.first
.dashboard__item
= react_admin_component :dimension,
dimension: 'instance_languages',
end_at: @time_period.last,
label: t('admin.instances.dashboard.instance_languages_dimension'),
limit: 8,
params: { domain: @instance.domain },
start_at: @time_period.first
- else - else
%p %p
= t('admin.instances.unknown_instance') = t('admin.instances.unknown_instance')

View file

@ -2,7 +2,7 @@
%td %td
.input-copy .input-copy
.input-copy__wrapper .input-copy__wrapper
%input{ type: :text, maxlength: '999', spellcheck: 'false', readonly: 'true', value: public_invite_url(invite_code: invite.code) } = copyable_input value: public_invite_url(invite_code: invite.code)
%button{ type: :button }= t('generic.copy') %button{ type: :button }= t('generic.copy')
%td %td

View file

@ -4,9 +4,7 @@
- content_for :heading_actions do - content_for :heading_actions do
- if current_user.can?(:view_dashboard) - if current_user.can?(:view_dashboard)
.time-period .time-period
= l(@time_period.first) = date_range(@time_period)
= ' - '
= l(@time_period.last)
= link_to t('admin.tags.open'), tag_url(@tag), class: 'button', target: '_blank', rel: 'noopener noreferrer' = link_to t('admin.tags.open'), tag_url(@tag), class: 'button', target: '_blank', rel: 'noopener noreferrer'

View file

@ -2,7 +2,7 @@
%td %td
.input-copy .input-copy
.input-copy__wrapper .input-copy__wrapper
%input{ type: :text, maxlength: '999', spellcheck: 'false', readonly: 'true', value: public_invite_url(invite_code: invite.code) } = copyable_input value: public_invite_url(invite_code: invite.code)
%button{ type: :button }= t('generic.copy') %button{ type: :button }= t('generic.copy')
- if invite.valid_for_use? - if invite.valid_for_use?

View file

@ -3,5 +3,5 @@
%p= t('doorkeeper.authorizations.show.title') %p= t('doorkeeper.authorizations.show.title')
.input-copy .input-copy
.input-copy__wrapper .input-copy__wrapper
%input.oauth-code{ type: 'text', spellcheck: 'false', readonly: true, value: params[:code] } = copyable_input value: params[:code], class: 'oauth-code'
%button{ type: :button }= t('generic.copy') %button{ type: :button }= t('generic.copy')

View file

@ -18,7 +18,7 @@
.field-group .field-group
.input.with_block_label .input.with_block_label
%label= t('activerecord.attributes.doorkeeper/application.scopes') = form.label t('activerecord.attributes.doorkeeper/application.scopes'), required: true
%span.hint= t('simple_form.hints.defaults.scopes') %span.hint= t('simple_form.hints.defaults.scopes')
- Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value| - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value|
@ -29,7 +29,7 @@
hint: false, hint: false,
include_blank: false, include_blank: false,
item_wrapper_tag: 'li', item_wrapper_tag: 'li',
label_method: ->(scope) { safe_join([content_tag(:samp, scope, class: class_for_scope(scope)), content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) }, label_method: ->(scope) { label_for_scope(scope) },
label: false, label: false,
required: false, required: false,
selected: form.object.scopes.all, selected: form.object.scopes.all,

View file

@ -18,7 +18,9 @@
- @applications.each do |application| - @applications.each do |application|
%tr %tr
%td= link_to application.name, settings_application_path(application) %td= link_to application.name, settings_application_path(application)
%th= application.scopes %th
- application.scopes.to_s.split.each do |scope|
= tag.samp(scope, class: 'information-badge', title: t("doorkeeper.scopes.#{scope}"))
%td %td
= table_link_to 'close', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') } = table_link_to 'close', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') }

View file

@ -15,15 +15,16 @@
%td %td
%code= @application.secret %code= @application.secret
%tr %tr
%th{ rowspan: 2 }= t('applications.your_token') %th= t('applications.your_token')
%td %td
%code= current_user.token_for_app(@application).token %code= current_user.token_for_app(@application).token
%tr %tr
%th
%td= table_link_to 'refresh', t('applications.regenerate_token'), regenerate_settings_application_path(@application), method: :post %td= table_link_to 'refresh', t('applications.regenerate_token'), regenerate_settings_application_path(@application), method: :post
%hr/ %hr/
= simple_form_for @application, url: settings_application_path(@application), method: :put do |form| = simple_form_for @application, url: settings_application_path(@application) do |form|
= render form = render form
.actions .actions

View file

@ -61,7 +61,8 @@
%tbody %tbody
- @backups.each do |backup| - @backups.each do |backup|
%tr %tr
%td= l backup.created_at %td
%time.formatted{ datetime: backup.created_at.iso8601, title: l(backup.created_at) }= l backup.created_at
- if backup.processed? - if backup.processed?
%td= number_to_human_size backup.dump_file_size %td= number_to_human_size backup.dump_file_size
%td= table_link_to 'download', t('exports.archive_takeout.download'), download_backup_url(backup) %td= table_link_to 'download', t('exports.archive_takeout.download'), download_backup_url(backup)

View file

@ -55,7 +55,10 @@
= t("imports.states.#{import.state}") = t("imports.states.#{import.state}")
%td %td
#{import.imported_items} / #{import.total_items} #{import.imported_items} / #{import.total_items}
%td= l(import.created_at) %td
%time.formatted{ datetime: import.created_at.iso8601, title: l(import.created_at) }
= l(import.created_at)
%td %td
- num_failed = import.processed_items - import.imported_items - num_failed = import.processed_items - import.imported_items
- if num_failed.positive? - if num_failed.positive?

View file

@ -4,7 +4,7 @@
- content_for :heading_actions do - content_for :heading_actions do
= button_tag t('generic.save_changes'), class: 'button', form: 'edit_user' = button_tag t('generic.save_changes'), class: 'button', form: 'edit_user'
= simple_form_for current_user, url: settings_preferences_appearance_path, html: { method: :put, id: 'edit_user' } do |f| = simple_form_for current_user, url: settings_preferences_appearance_path, html: { id: :edit_user } do |f|
.fields-row .fields-row
.fields-group.fields-row__column.fields-row__column-6 .fields-group.fields-row__column.fields-row__column-6
= f.input :locale, = f.input :locale,

View file

@ -4,7 +4,7 @@
- content_for :heading_actions do - content_for :heading_actions do
= button_tag t('generic.save_changes'), class: 'button', form: 'edit_notification' = button_tag t('generic.save_changes'), class: 'button', form: 'edit_notification'
= simple_form_for current_user, url: settings_preferences_notifications_path, html: { method: :put, id: 'edit_notification' } do |f| = simple_form_for current_user, url: settings_preferences_notifications_path, html: { id: :edit_notification } do |f|
= render 'shared/error_messages', object: current_user = render 'shared/error_messages', object: current_user
%h4= t 'notifications.email_events' %h4= t 'notifications.email_events'

View file

@ -4,7 +4,7 @@
- content_for :heading_actions do - content_for :heading_actions do
= button_tag t('generic.save_changes'), class: 'button', form: 'edit_preferences' = button_tag t('generic.save_changes'), class: 'button', form: 'edit_preferences'
= simple_form_for current_user, url: settings_preferences_other_path, html: { method: :put, id: 'edit_preferences' } do |f| = simple_form_for current_user, url: settings_preferences_other_path, html: { id: :edit_preferences } do |f|
= render 'shared/error_messages', object: current_user = render 'shared/error_messages', object: current_user
= f.simple_fields_for :settings, current_user.settings do |ff| = f.simple_fields_for :settings, current_user.settings do |ff|

View file

@ -5,7 +5,7 @@
%h2= t('settings.profile') %h2= t('settings.profile')
= render partial: 'settings/shared/profile_navigation' = render partial: 'settings/shared/profile_navigation'
= simple_form_for @account, url: settings_privacy_path, html: { method: :put } do |f| = simple_form_for @account, url: settings_privacy_path do |f|
= render 'shared/error_messages', object: @account = render 'shared/error_messages', object: @account
%p.lead= t('privacy.hint_html') %p.lead= t('privacy.hint_html')

View file

@ -5,7 +5,7 @@
%h2= t('settings.profile') %h2= t('settings.profile')
= render partial: 'settings/shared/profile_navigation' = render partial: 'settings/shared/profile_navigation'
= simple_form_for @account, url: settings_profile_path, html: { method: :put, id: 'edit_profile' } do |f| = simple_form_for @account, url: settings_profile_path, html: { id: :edit_profile } do |f|
= render 'shared/error_messages', object: @account = render 'shared/error_messages', object: @account
%p.lead= t('edit_profile.hint_html') %p.lead= t('edit_profile.hint_html')
@ -34,7 +34,7 @@
.fields-row__column.fields-row__column-6 .fields-row__column.fields-row__column-6
.fields-group .fields-group
= f.input :avatar, = f.input :avatar,
hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(Account::Avatar::LIMIT)), hint: t('simple_form.hints.defaults.avatar', dimensions: Account::Avatar::AVATAR_GEOMETRY, size: number_to_human_size(Account::Avatar::LIMIT)),
input_html: { accept: Account::Avatar::IMAGE_MIME_TYPES.join(',') }, input_html: { accept: Account::Avatar::IMAGE_MIME_TYPES.join(',') },
wrapper: :with_block_label wrapper: :with_block_label
@ -50,7 +50,7 @@
.fields-row__column.fields-row__column-6 .fields-row__column.fields-row__column-6
.fields-group .fields-group
= f.input :header, = f.input :header,
hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(Account::Header::LIMIT)), hint: t('simple_form.hints.defaults.header', dimensions: Account::Header::HEADER_GEOMETRY, size: number_to_human_size(Account::Header::LIMIT)),
input_html: { accept: Account::Header::IMAGE_MIME_TYPES.join(',') }, input_html: { accept: Account::Header::IMAGE_MIME_TYPES.join(',') },
wrapper: :with_block_label wrapper: :with_block_label

View file

@ -1,7 +1,7 @@
.content__heading__tabs .content__heading__tabs
= render_navigation renderer: :links do |primary| = render_navigation renderer: :links do |primary|
:ruby :ruby
primary.item :profile, safe_join([material_symbol('person'), t('settings.edit_profile')]), settings_profile_path primary.item :edit_profile, safe_join([material_symbol('person'), t('settings.edit_profile')]), settings_profile_path
primary.item :privacy, safe_join([material_symbol('lock'), t('privacy.title')]), settings_privacy_path primary.item :privacy_reach, safe_join([material_symbol('lock'), t('privacy.title')]), settings_privacy_path
primary.item :verification, safe_join([material_symbol('check'), t('verification.verification')]), settings_verification_path primary.item :verification, safe_join([material_symbol('check'), t('verification.verification')]), settings_verification_path
primary.item :featured_tags, safe_join([material_symbol('tag'), t('settings.featured_tags')]), settings_featured_tags_path primary.item :featured_tags, safe_join([material_symbol('tag'), t('settings.featured_tags')]), settings_featured_tags_path

View file

@ -5,7 +5,7 @@
%p.hint= t('otp_authentication.instructions_html') %p.hint= t('otp_authentication.instructions_html')
.qr-wrapper .qr-wrapper
.qr-code!= @qrcode.as_svg(padding: 0, module_size: 4) .qr-code!= @qrcode.as_svg(padding: 0, module_size: 4, use_path: true)
.qr-alternative .qr-alternative
%p.hint= t('otp_authentication.manual_instructions') %p.hint= t('otp_authentication.manual_instructions')

View file

@ -16,7 +16,7 @@
.input-copy.lead .input-copy.lead
.input-copy__wrapper .input-copy__wrapper
%input{ type: :text, maxlength: '999', spellcheck: 'false', readonly: 'true', value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: 'me').to_str } = copyable_input value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: :me)
%button{ type: :button }= t('generic.copy') %button{ type: :button }= t('generic.copy')
%p.lead= t('verification.extra_instructions_html') %p.lead= t('verification.extra_instructions_html')
@ -31,7 +31,7 @@
= material_symbol 'check', class: 'verified-badge__mark' = material_symbol 'check', class: 'verified-badge__mark'
%span= field.value %span= field.value
= simple_form_for @account, url: settings_verification_path, html: { method: :put, class: 'form-section' } do |f| = simple_form_for @account, url: settings_verification_path, html: { class: 'form-section' } do |f|
= render 'shared/error_messages', object: @account = render 'shared/error_messages', object: @account
%h3= t('author_attribution.title') %h3= t('author_attribution.title')
@ -50,13 +50,13 @@
= image_tag frontend_asset_url('images/preview.png'), alt: '', class: 'status-card__image-image' = image_tag frontend_asset_url('images/preview.png'), alt: '', class: 'status-card__image-image'
.status-card__content .status-card__content
%span.status-card__host %span.status-card__host
%span= t('author_attribution.s_blog', name: @account.username) %span= t('author_attribution.s_blog', name: display_name(@account))
· ·
%time.time-ago{ datetime: 1.year.ago.to_date.iso8601 } %time.time-ago{ datetime: 1.year.ago.to_date.iso8601 }
%strong.status-card__title= t('author_attribution.example_title') %strong.status-card__title= t('author_attribution.example_title')
.more-from-author .more-from-author
= logo_as_symbol(:icon) = logo_as_symbol(:icon)
= t('author_attribution.more_from_html', name: link_to(root_url, class: 'story__details__shared__author-link') { image_tag(@account.avatar.url, class: 'account__avatar', width: 16, height: 16, alt: '') + content_tag(:bdi, display_name(@account)) }) = t('author_attribution.more_from_html', name: link_to(root_url, class: 'story__details__shared__author-link') { image_tag(@account.avatar.url, class: 'account__avatar', width: 16, height: 16, alt: '') + tag.bdi(display_name(@account)) })
.actions .actions
= f.button :button, t('generic.save_changes'), type: :submit = f.button :button, t('generic.save_changes'), type: :submit

Some files were not shown because too many files have changed in this diff Show more