Merge pull request #2865 from ClearlyClaire/glitch-soc/backports-4.3
Merge upstream changes (stable-4.3)
|
@ -50,7 +50,7 @@ jobs:
|
|||
|
||||
# Create or update the 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:
|
||||
commit-message: 'New Crowdin translations'
|
||||
title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)'
|
||||
|
|
2
.github/workflows/crowdin-download.yml
vendored
|
@ -53,7 +53,7 @@ jobs:
|
|||
|
||||
# Create or update the 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:
|
||||
commit-message: 'New Crowdin translations'
|
||||
title: 'New Crowdin Translations (automated)'
|
||||
|
|
14
Gemfile.lock
|
@ -601,7 +601,7 @@ GEM
|
|||
actionmailer (>= 3)
|
||||
net-smtp
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
propshaft (1.0.1)
|
||||
propshaft (1.1.0)
|
||||
actionpack (>= 7.0.0)
|
||||
activesupport (>= 7.0.0)
|
||||
rack
|
||||
|
@ -698,7 +698,7 @@ GEM
|
|||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.3.7)
|
||||
rexml (3.3.8)
|
||||
rotp (6.3.0)
|
||||
rouge (4.3.0)
|
||||
rpam2 (4.0.2)
|
||||
|
@ -748,15 +748,15 @@ GEM
|
|||
parser (>= 3.3.1.0)
|
||||
rubocop-capybara (2.21.0)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-performance (1.21.1)
|
||||
rubocop-performance (1.22.1)
|
||||
rubocop (>= 1.48.1, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rails (2.25.1)
|
||||
rubocop-rails (2.26.2)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop (>= 1.52.0, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rspec (3.0.4)
|
||||
rubocop-rspec (3.0.5)
|
||||
rubocop (~> 1.61)
|
||||
rubocop-rspec_rails (2.30.0)
|
||||
rubocop (~> 1.61)
|
||||
|
@ -884,7 +884,7 @@ GEM
|
|||
webfinger (1.2.0)
|
||||
activesupport
|
||||
httpclient (>= 2.4)
|
||||
webmock (3.23.1)
|
||||
webmock (3.24.0)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
|
|
@ -13,7 +13,7 @@ module WebAppControllerConcern
|
|||
policy = ContentSecurityPolicy.new
|
||||
|
||||
if policy.sso_host.present?
|
||||
p.form_action policy.sso_host
|
||||
p.form_action policy.sso_host, -> { "https://#{request.host}/auth/auth/" }
|
||||
else
|
||||
p.form_action :none
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ module Settings
|
|||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
|
|
@ -7,7 +7,23 @@ module WellKnown
|
|||
def show
|
||||
@webfinger_template = "#{webfinger_url}?resource={uri}"
|
||||
expires_in 3.days, public: true
|
||||
render content_type: 'application/xrd+xml', formats: [:xml]
|
||||
|
||||
respond_to do |format|
|
||||
format.any do
|
||||
render content_type: 'application/xrd+xml', formats: [:xml]
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: {
|
||||
links: [
|
||||
{
|
||||
rel: 'lrdd',
|
||||
template: @webfinger_template,
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,4 +35,11 @@ module Admin::ActionLogsHelper
|
|||
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
|
||||
|
|
|
@ -18,6 +18,11 @@ module Admin::DashboardHelper
|
|||
end
|
||||
end
|
||||
|
||||
def date_range(range)
|
||||
[l(range.first), l(range.last)]
|
||||
.join(' - ')
|
||||
end
|
||||
|
||||
def relevant_account_timestamp(account)
|
||||
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]
|
||||
|
@ -25,6 +30,8 @@ module Admin::DashboardHelper
|
|||
[account.user_current_sign_in_at, false]
|
||||
elsif account.user_pending?
|
||||
[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?
|
||||
[account.last_status_at, true]
|
||||
else
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ApplicationHelper
|
||||
DANGEROUS_SCOPES = %w(
|
||||
read
|
||||
write
|
||||
follow
|
||||
).freeze
|
||||
|
||||
RTL_LOCALES = %i(
|
||||
ar
|
||||
ckb
|
||||
|
@ -95,8 +89,11 @@ module ApplicationHelper
|
|||
Rails.env.production? ? site_title : "#{site_title} (Dev)"
|
||||
end
|
||||
|
||||
def class_for_scope(scope)
|
||||
'scope-danger' if DANGEROUS_SCOPES.include?(scope.to_s)
|
||||
def label_for_scope(scope)
|
||||
safe_join [
|
||||
tag.samp(scope, class: { 'scope-danger' => SessionActivation::DEFAULT_SCOPES.include?(scope.to_s) }),
|
||||
tag.span(t("doorkeeper.scopes.#{scope}"), class: :hint),
|
||||
]
|
||||
end
|
||||
|
||||
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'))
|
||||
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
|
||||
def preload_locale_pack
|
||||
supported_locales = Themes.instance.flavour(current_flavour)['locales']
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WebfingerHelper
|
||||
def webfinger!(uri)
|
||||
Webfinger.new(uri).perform
|
||||
end
|
||||
end
|
|
@ -37,8 +37,7 @@ export const synchronouslySubmitMarkers = createAppAsyncThunk(
|
|||
});
|
||||
|
||||
return;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if ('navigator' && 'sendBeacon' in navigator) {
|
||||
} else if ('sendBeacon' in navigator) {
|
||||
// Failing that, we can use sendBeacon, but we have to encode the data as
|
||||
// FormData for DoorKeeper to recognize the token.
|
||||
const formData = new FormData();
|
||||
|
|
|
@ -70,6 +70,10 @@ function dispatchAssociatedRecords(
|
|||
|
||||
const supportedGroupedNotificationTypes = ['favourite', 'reblog'];
|
||||
|
||||
export function shouldGroupNotificationType(type: string) {
|
||||
return supportedGroupedNotificationTypes.includes(type);
|
||||
}
|
||||
|
||||
export const fetchNotifications = createDataLoadingThunk(
|
||||
'notificationGroups/fetch',
|
||||
async (_params, { getState }) =>
|
||||
|
|
|
@ -198,7 +198,7 @@ class Item extends PureComponent {
|
|||
|
||||
{visible && thumbnail}
|
||||
|
||||
{badges && (
|
||||
{visible && badges && (
|
||||
<div className='media-gallery__item__badges'>
|
||||
{badges}
|
||||
</div>
|
||||
|
|
|
@ -507,7 +507,7 @@ export default function compose(state = initialState, action) {
|
|||
.set('isUploadingThumbnail', false)
|
||||
.update('media_attachments', list => list.map(item => {
|
||||
if (item.get('id') === action.media.id) {
|
||||
return fromJS(action.media);
|
||||
return fromJS(action.media).set('unattached', item.get('unattached'));
|
||||
}
|
||||
|
||||
return item;
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
unmountNotifications,
|
||||
refreshStaleNotificationGroups,
|
||||
pollRecentNotifications,
|
||||
shouldGroupNotificationType,
|
||||
} from 'flavours/glitch/actions/notification_groups';
|
||||
import {
|
||||
disconnectTimeline,
|
||||
|
@ -205,6 +206,13 @@ function processNewNotification(
|
|||
groups: NotificationGroupsState['groups'],
|
||||
notification: ApiNotificationJSON,
|
||||
) {
|
||||
if (!shouldGroupNotificationType(notification.type)) {
|
||||
notification = {
|
||||
...notification,
|
||||
group_key: `ungrouped-${notification.id}`,
|
||||
};
|
||||
}
|
||||
|
||||
const existingGroupIndex = groups.findIndex(
|
||||
(group) =>
|
||||
group.type !== 'gap' && group.group_key === notification.group_key,
|
||||
|
@ -242,7 +250,7 @@ function processNewNotification(
|
|||
groups.unshift(existingGroup);
|
||||
}
|
||||
} else {
|
||||
// Create a new group
|
||||
// We have not found an existing group, create a new one
|
||||
groups.unshift(createNotificationGroupFromNotificationJSON(notification));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -226,6 +226,10 @@ $content-width: 840px;
|
|||
gap: 5px;
|
||||
white-space: nowrap;
|
||||
|
||||
@media screen and (max-width: $mobile-breakpoint) {
|
||||
flex: 1 0 50%;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
|
@ -1075,6 +1079,10 @@ a.name-tag,
|
|||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
a.announcements-list__item__title {
|
||||
&:hover,
|
||||
&:focus,
|
||||
|
|
|
@ -3793,6 +3793,7 @@ $ui-header-logo-wordmark-width: 99px;
|
|||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.drawer__inner__mastodon {
|
||||
|
|
|
@ -137,6 +137,7 @@ a.table-action-link {
|
|||
padding: 0 10px;
|
||||
color: $darker-text-color;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: $highlight-text-color;
|
||||
|
|
|
@ -37,8 +37,7 @@ export const synchronouslySubmitMarkers = createAppAsyncThunk(
|
|||
});
|
||||
|
||||
return;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if ('navigator' && 'sendBeacon' in navigator) {
|
||||
} else if ('sendBeacon' in navigator) {
|
||||
// Failing that, we can use sendBeacon, but we have to encode the data as
|
||||
// FormData for DoorKeeper to recognize the token.
|
||||
const formData = new FormData();
|
||||
|
|
|
@ -70,6 +70,10 @@ function dispatchAssociatedRecords(
|
|||
|
||||
const supportedGroupedNotificationTypes = ['favourite', 'reblog'];
|
||||
|
||||
export function shouldGroupNotificationType(type: string) {
|
||||
return supportedGroupedNotificationTypes.includes(type);
|
||||
}
|
||||
|
||||
export const fetchNotifications = createDataLoadingThunk(
|
||||
'notificationGroups/fetch',
|
||||
async (_params, { getState }) =>
|
||||
|
|
|
@ -196,7 +196,7 @@ class Item extends PureComponent {
|
|||
|
||||
{visible && thumbnail}
|
||||
|
||||
{badges && (
|
||||
{visible && badges && (
|
||||
<div className='media-gallery__item__badges'>
|
||||
{badges}
|
||||
</div>
|
||||
|
|
|
@ -402,7 +402,7 @@ export default function compose(state = initialState, action) {
|
|||
.set('isUploadingThumbnail', false)
|
||||
.update('media_attachments', list => list.map(item => {
|
||||
if (item.get('id') === action.media.id) {
|
||||
return fromJS(action.media);
|
||||
return fromJS(action.media).set('unattached', item.get('unattached'));
|
||||
}
|
||||
|
||||
return item;
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
unmountNotifications,
|
||||
refreshStaleNotificationGroups,
|
||||
pollRecentNotifications,
|
||||
shouldGroupNotificationType,
|
||||
} from 'mastodon/actions/notification_groups';
|
||||
import {
|
||||
disconnectTimeline,
|
||||
|
@ -205,6 +206,13 @@ function processNewNotification(
|
|||
groups: NotificationGroupsState['groups'],
|
||||
notification: ApiNotificationJSON,
|
||||
) {
|
||||
if (!shouldGroupNotificationType(notification.type)) {
|
||||
notification = {
|
||||
...notification,
|
||||
group_key: `ungrouped-${notification.id}`,
|
||||
};
|
||||
}
|
||||
|
||||
const existingGroupIndex = groups.findIndex(
|
||||
(group) =>
|
||||
group.type !== 'gap' && group.group_key === notification.group_key,
|
||||
|
@ -242,7 +250,7 @@ function processNewNotification(
|
|||
groups.unshift(existingGroup);
|
||||
}
|
||||
} else {
|
||||
// Create a new group
|
||||
// We have not found an existing group, create a new one
|
||||
groups.unshift(createNotificationGroupFromNotificationJSON(notification));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/cloud-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/computer-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/database-fill.svg
Normal 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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/diamond-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/groups-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/inbox-fill.svg
Normal 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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/list-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/mood-fill.svg
Normal 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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/report-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
1
app/javascript/material-icons/400-24px/speed-fill.svg
Normal 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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -226,6 +226,10 @@ $content-width: 840px;
|
|||
gap: 5px;
|
||||
white-space: nowrap;
|
||||
|
||||
@media screen and (max-width: $mobile-breakpoint) {
|
||||
flex: 1 0 50%;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
|
@ -1070,6 +1074,10 @@ a.name-tag,
|
|||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
a.announcements-list__item__title {
|
||||
&:hover,
|
||||
&:focus,
|
||||
|
|
|
@ -3611,6 +3611,7 @@ $ui-header-logo-wordmark-width: 99px;
|
|||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.drawer__inner__mastodon {
|
||||
|
|
|
@ -137,6 +137,7 @@ a.table-action-link {
|
|||
padding: 0 10px;
|
||||
color: $darker-text-color;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: $highlight-text-color;
|
||||
|
|
|
@ -42,6 +42,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
def process_status
|
||||
@tags = []
|
||||
@mentions = []
|
||||
@unresolved_mentions = []
|
||||
@silenced_account_ids = []
|
||||
@params = {}
|
||||
|
||||
|
@ -55,6 +56,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
end
|
||||
|
||||
resolve_thread(@status)
|
||||
resolve_unresolved_mentions(@status)
|
||||
fetch_replies(@status)
|
||||
distribute
|
||||
forward_for_reply
|
||||
|
@ -197,6 +199,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
return if account.nil?
|
||||
|
||||
@mentions << Mention.new(account: account, silent: false)
|
||||
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError
|
||||
@unresolved_mentions << tag['href']
|
||||
end
|
||||
|
||||
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] })
|
||||
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)
|
||||
collection = @object['replies']
|
||||
return if collection.blank?
|
||||
|
|
|
@ -9,10 +9,10 @@ class Vacuum::ImportsVacuum
|
|||
private
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
72
app/lib/web_push_request.rb
Normal 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
|
|
@ -21,7 +21,7 @@ class AccountFilter
|
|||
end
|
||||
|
||||
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|
|
||||
next if key.to_s == 'page'
|
||||
|
|
|
@ -14,7 +14,7 @@ class Admin::TagFilter
|
|||
end
|
||||
|
||||
def results
|
||||
scope = Tag.reorder(nil)
|
||||
scope = Tag.all
|
||||
|
||||
params.each do |key, value|
|
||||
next if key == :page
|
||||
|
|
|
@ -6,10 +6,13 @@ module Account::Avatar
|
|||
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
|
||||
LIMIT = 2.megabytes
|
||||
|
||||
AVATAR_DIMENSIONS = [400, 400].freeze
|
||||
AVATAR_GEOMETRY = [AVATAR_DIMENSIONS.first, AVATAR_DIMENSIONS.last].join('x')
|
||||
|
||||
class_methods do
|
||||
def avatar_styles(file)
|
||||
styles = { original: { geometry: '400x400#', file_geometry_parser: FastGeometryParser } }
|
||||
styles[:static] = { geometry: '400x400#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
|
||||
styles = { original: { geometry: "#{AVATAR_GEOMETRY}#", file_geometry_parser: FastGeometryParser } }
|
||||
styles[:static] = { geometry: "#{AVATAR_GEOMETRY}#", format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
|
||||
styles
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@ module Account::Header
|
|||
|
||||
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
|
||||
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
|
||||
def header_styles(file)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
class RemoteFollow
|
||||
include ActiveModel::Validations
|
||||
include RoutingHelper
|
||||
include WebfingerHelper
|
||||
|
||||
attr_accessor :acct, :addressable_template
|
||||
|
||||
|
@ -66,7 +65,7 @@ class RemoteFollow
|
|||
end
|
||||
|
||||
def acct_resource
|
||||
@acct_resource ||= webfinger!("acct:#{acct}")
|
||||
@acct_resource ||= Webfinger.new("acct:#{acct}").perform
|
||||
rescue Webfinger::Error, HTTP::ConnectionError
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -18,13 +18,25 @@ class ReportFilter
|
|||
def results
|
||||
scope = Report.unresolved
|
||||
|
||||
params.each do |key, value|
|
||||
relevant_params.each do |key, value|
|
||||
scope = scope.merge scope_for(key, value)
|
||||
end
|
||||
|
||||
scope
|
||||
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)
|
||||
case key.to_sym
|
||||
when :by_target_domain
|
||||
|
|
|
@ -28,6 +28,8 @@ class SessionActivation < ApplicationRecord
|
|||
|
||||
before_create :assign_access_token
|
||||
|
||||
DEFAULT_SCOPES = %w(read write follow).freeze
|
||||
|
||||
class << self
|
||||
def active?(id)
|
||||
id && exists?(session_id: id)
|
||||
|
@ -64,7 +66,7 @@ class SessionActivation < ApplicationRecord
|
|||
{
|
||||
application_id: Doorkeeper::Application.find_by(superapp: true)&.id,
|
||||
resource_owner_id: user_id,
|
||||
scopes: 'read write follow',
|
||||
scopes: DEFAULT_SCOPES.join(' '),
|
||||
expires_in: Doorkeeper.configuration.access_token_expires_in,
|
||||
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?,
|
||||
}
|
||||
|
|
|
@ -71,7 +71,8 @@ class User < ApplicationRecord
|
|||
ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days.freeze
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -29,26 +29,6 @@ class Web::PushSubscription < ApplicationRecord
|
|||
|
||||
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)
|
||||
policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification)
|
||||
end
|
||||
|
@ -92,14 +72,6 @@ class Web::PushSubscription < ApplicationRecord
|
|||
)
|
||||
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)
|
||||
truthy?(data&.dig('alerts', notification.type.to_s))
|
||||
end
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
class ActivityPub::FetchRemoteActorService < BaseService
|
||||
include JsonLdHelper
|
||||
include DomainControlHelper
|
||||
include WebfingerHelper
|
||||
|
||||
class Error < StandardError; end
|
||||
|
||||
|
@ -45,7 +44,7 @@ class ActivityPub::FetchRemoteActorService < BaseService
|
|||
private
|
||||
|
||||
def check_webfinger!
|
||||
webfinger = webfinger!("acct:#{@username}@#{@domain}")
|
||||
webfinger = Webfinger.new("acct:#{@username}@#{@domain}").perform
|
||||
confirmed_username, confirmed_domain = split_acct(webfinger.subject)
|
||||
|
||||
if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero?
|
||||
|
@ -54,7 +53,7 @@ class ActivityPub::FetchRemoteActorService < BaseService
|
|||
return
|
||||
end
|
||||
|
||||
webfinger = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}")
|
||||
webfinger = Webfinger.new("acct:#{confirmed_username}@#{confirmed_domain}").perform
|
||||
@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?
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class ActivityPub::ProcessCollectionService < BaseService
|
||||
include JsonLdHelper
|
||||
include DomainControlHelper
|
||||
|
||||
def call(body, actor, **options)
|
||||
@account = actor
|
||||
|
@ -69,6 +70,9 @@ class ActivityPub::ProcessCollectionService < BaseService
|
|||
end
|
||||
|
||||
def verify_account!
|
||||
return unless @json['signature'].is_a?(Hash)
|
||||
return if domain_not_allowed?(@json['signature']['creator'])
|
||||
|
||||
@options[:relayed_through_actor] = @account
|
||||
@account = ActivityPub::LinkedDataSignature.new(@json).verify_actor!
|
||||
@account = nil unless @account.is_a?(Account)
|
||||
|
|
|
@ -16,12 +16,12 @@ class PurgeDomainService < BaseService
|
|||
end
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
def purge_emojis!
|
||||
CustomEmoji.remote.where(domain: @domain).reorder(nil).find_each(&:destroy)
|
||||
CustomEmoji.remote.where(domain: @domain).find_each(&:destroy)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class ResolveAccountService < BaseService
|
||||
include DomainControlHelper
|
||||
include WebfingerHelper
|
||||
include Redisable
|
||||
include Lockable
|
||||
|
||||
|
@ -81,7 +80,7 @@ class ResolveAccountService < BaseService
|
|||
end
|
||||
|
||||
def process_webfinger!(uri)
|
||||
@webfinger = webfinger!("acct:#{uri}")
|
||||
@webfinger = Webfinger.new("acct:#{uri}").perform
|
||||
confirmed_username, confirmed_domain = split_acct(@webfinger.subject)
|
||||
|
||||
if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
|
||||
|
@ -91,7 +90,7 @@ class ResolveAccountService < BaseService
|
|||
end
|
||||
|
||||
# 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)
|
||||
|
||||
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?
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
%strong= t('admin.action_logs.filter_by_action')
|
||||
.input.select.optional
|
||||
= 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')
|
||||
|
||||
- if @action_logs.empty?
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
= t('admin.dashboard.title')
|
||||
|
||||
- content_for :heading_actions do
|
||||
= l(@time_period.first)
|
||||
= ' - '
|
||||
= l(@time_period.last)
|
||||
= date_range(@time_period)
|
||||
|
||||
- unless @system_checks.empty?
|
||||
.flash-message-stack
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
label: I18n.t('admin.email_domain_blocks.allow_registrations_with_approval'),
|
||||
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')
|
||||
|
||||
.batch-table
|
||||
|
|
66
app/views/admin/instances/_dashboard.html.haml
Normal 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
|
|
@ -3,77 +3,10 @@
|
|||
|
||||
- if current_user.can?(:view_dashboard)
|
||||
- content_for :heading_actions do
|
||||
= l(@time_period.first)
|
||||
= ' - '
|
||||
= l(@time_period.last)
|
||||
= date_range(@time_period)
|
||||
|
||||
- if @instance.persisted?
|
||||
%p
|
||||
= 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
|
||||
|
||||
= render 'dashboard', instance_domain: @instance.domain, period_end_at: @time_period.last, period_start_at: @time_period.first
|
||||
- else
|
||||
%p
|
||||
= t('admin.instances.unknown_instance')
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
%td
|
||||
.input-copy
|
||||
.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')
|
||||
|
||||
%td
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
- content_for :heading_actions do
|
||||
- if current_user.can?(:view_dashboard)
|
||||
.time-period
|
||||
= l(@time_period.first)
|
||||
= ' - '
|
||||
= l(@time_period.last)
|
||||
= date_range(@time_period)
|
||||
|
||||
= link_to t('admin.tags.open'), tag_url(@tag), class: 'button', target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
%td
|
||||
.input-copy
|
||||
.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')
|
||||
|
||||
- if invite.valid_for_use?
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
%p= t('doorkeeper.authorizations.show.title')
|
||||
.input-copy
|
||||
.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')
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
.field-group
|
||||
.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')
|
||||
|
||||
- Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value|
|
||||
|
@ -29,7 +29,7 @@
|
|||
hint: false,
|
||||
include_blank: false,
|
||||
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,
|
||||
required: false,
|
||||
selected: form.object.scopes.all,
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
- @applications.each do |application|
|
||||
%tr
|
||||
%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
|
||||
= table_link_to 'close', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') }
|
||||
|
||||
|
|
|
@ -15,15 +15,16 @@
|
|||
%td
|
||||
%code= @application.secret
|
||||
%tr
|
||||
%th{ rowspan: 2 }= t('applications.your_token')
|
||||
%th= t('applications.your_token')
|
||||
%td
|
||||
%code= current_user.token_for_app(@application).token
|
||||
%tr
|
||||
%th
|
||||
%td= table_link_to 'refresh', t('applications.regenerate_token'), regenerate_settings_application_path(@application), method: :post
|
||||
|
||||
%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
|
||||
|
||||
.actions
|
||||
|
|
|
@ -61,7 +61,8 @@
|
|||
%tbody
|
||||
- @backups.each do |backup|
|
||||
%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?
|
||||
%td= number_to_human_size backup.dump_file_size
|
||||
%td= table_link_to 'download', t('exports.archive_takeout.download'), download_backup_url(backup)
|
||||
|
|
|
@ -55,7 +55,10 @@
|
|||
= t("imports.states.#{import.state}")
|
||||
%td
|
||||
#{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
|
||||
- num_failed = import.processed_items - import.imported_items
|
||||
- if num_failed.positive?
|
||||
|
|