Compare commits

..

3 Commits

Author SHA1 Message Date
Rin 2c8d15c6cf
Fix non-rendering on certain engines 2022-11-13 13:09:40 +11:00
Rin 4c1d29a73e
Change Safari bookmark colour to match treehouse 2022-11-13 12:47:59 +11:00
Rin b22c3e1401
Add small icon assets 2022-11-13 12:46:05 +11:00
24 changed files with 46 additions and 174 deletions

View File

@ -1,18 +0,0 @@
pipeline:
build:
image: docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock
commands:
- docker image build -f Dockerfile . -t gitea.treehouse.systems/treehouse/mastodon:latest
push:
image: docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock
commands:
- echo $REGISTRY_SECRET | docker login -u ariadne --password-stdin gitea.treehouse.systems
- docker image push --all-tags gitea.treehouse.systems/treehouse/mastodon
when:
event: [push, tag]
secrets: [REGISTRY_SECRET]

View File

@ -51,15 +51,6 @@ class LanguageDropdownMenu extends React.PureComponent {
document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
this.setState({ mounted: true });
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
// to wait for a frame before focusing
requestAnimationFrame(() => {
if (this.node) {
const element = this.node.querySelector('input[type="search"]');
if (element) element.focus();
}
});
}
componentWillUnmount () {
@ -235,7 +226,7 @@ class LanguageDropdownMenu extends React.PureComponent {
// react-overlays
<div className={`language-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
<div className='emoji-mart-search'>
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} autoFocus />
<button className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
</div>

View File

@ -573,7 +573,6 @@ export default function compose(state = initialState, action) {
'advanced_options',
map => map.merge(new ImmutableMap({ do_not_federate }))
);
map.set('id', null);
if (action.status.get('spoiler_text').length > 0) {
map.set('spoiler', true);

View File

@ -268,8 +268,7 @@ html {
.status__content .status__content__spoiler-link {
background: $ui-base-color;
&:hover,
&:focus {
&:hover {
background: lighten($ui-base-color, 4%);
}
}

View File

@ -51,15 +51,6 @@ class LanguageDropdownMenu extends React.PureComponent {
document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
this.setState({ mounted: true });
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
// to wait for a frame before focusing
requestAnimationFrame(() => {
if (this.node) {
const element = this.node.querySelector('input[type="search"]');
if (element) element.focus();
}
});
}
componentWillUnmount () {
@ -235,7 +226,7 @@ class LanguageDropdownMenu extends React.PureComponent {
// react-overlays
<div className={`language-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
<div className='emoji-mart-search'>
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} autoFocus />
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
</div>

View File

@ -452,7 +452,6 @@ export default function compose(state = initialState, action) {
map.set('idempotencyKey', uuid());
map.set('sensitive', action.status.get('sensitive'));
map.set('language', action.status.get('language'));
map.set('id', null);
if (action.status.get('spoiler_text').length > 0) {
map.set('spoiler', true);

View File

@ -0,0 +1,4 @@
@import 'flavours/glitch/styles/application';
@media (prefers-color-scheme: light) {
@import 'flavours/glitch/styles/mastodon-light';
}

View File

@ -0,0 +1,4 @@
en:
skins:
glitch:
mastodon-system: Mastodon (system-preferred)

View File

@ -4,5 +4,5 @@ en:
mastodon-light: Mastodon (light)
es:
skins:
glitch:
vanilla:
mastodon-light: Mastodon (claro)

View File

@ -0,0 +1,4 @@
@import 'styles/application';
@media (prefers-color-scheme: light) {
@import 'styles/mastodon-light';
}

View File

@ -0,0 +1,4 @@
en:
skins:
vanilla:
mastodon-system: Mastodon (system-preferred)

View File

@ -268,8 +268,7 @@ html {
.status__content .status__content__spoiler-link {
background: $ui-base-color;
&:hover,
&:focus {
&:hover {
background: lighten($ui-base-color, 4%);
}
}

View File

@ -62,6 +62,8 @@ class Request
end
begin
response = response.extend(ClientLimit)
# If we are using a persistent connection, we have to
# read every response to be able to move forward at all.
# However, simply calling #to_s or #flush may not be safe,
@ -179,14 +181,6 @@ class Request
end
end
if ::HTTP::Response.methods.include?(:body_with_limit) && !Rails.env.production?
abort 'HTTP::Response#body_with_limit is already defined, the monkey patch will not be applied'
else
class ::HTTP::Response
include Request::ClientLimit
end
end
class Socket < TCPSocket
class << self
def open(host, *args)

View File

@ -139,12 +139,7 @@ class AccountStatusesCleanupPolicy < ApplicationRecord
# Filtering on `id` rather than `min_status_age` ago will treat
# non-snowflake statuses as older than they really are, but Mastodon
# has switched to snowflake IDs significantly over 2 years ago anyway.
snowflake_id = Mastodon::Snowflake.id_at(min_status_age.seconds.ago, with_random: false)
if max_id.nil? || snowflake_id < max_id
max_id = snowflake_id
end
max_id = [max_id, Mastodon::Snowflake.id_at(min_status_age.seconds.ago, with_random: false)].compact.min
Status.where(Status.arel_table[:id].lteq(max_id))
end

View File

@ -63,8 +63,6 @@ class FeaturedTag < ApplicationRecord
end
def validate_featured_tags_limit
return unless account.local?
errors.add(:base, I18n.t('featured_tags.errors.limit')) if account.featured_tags.count >= LIMIT
end

View File

@ -51,17 +51,21 @@ class ActivityPub::FetchFeaturedTagsCollectionService < BaseService
end
def process_items(items)
names = items.filter_map { |item| item['type'] == 'Hashtag' && item['name']&.delete_prefix('#') }.take(FeaturedTag::LIMIT)
tags = names.index_by { |name| HashtagNormalizer.new.normalize(name) }
normalized_names = tags.keys
names = items.filter_map { |item| item['type'] == 'Hashtag' && item['name']&.delete_prefix('#') }.map { |name| HashtagNormalizer.new.normalize(name) }
to_remove = []
to_add = names
FeaturedTag.includes(:tag).references(:tag).where(account: @account).where.not(tag: { name: normalized_names }).delete_all
FeaturedTag.includes(:tag).references(:tag).where(account: @account, tag: { name: normalized_names }).each do |featured_tag|
featured_tag.update(name: tags.delete(featured_tag.tag.name))
FeaturedTag.where(account: @account).map(&:name).each do |name|
if names.include?(name)
to_add.delete(name)
else
to_remove << name
end
end
tags.each_value do |name|
FeaturedTag.includes(:tag).where(account: @account, tags: { name: to_remove }).delete_all unless to_remove.empty?
to_add.each do |name|
FeaturedTag.create!(account: @account, name: name)
end
end

View File

@ -5,7 +5,7 @@
= f.input :return_to, as: :hidden
.field-group
= f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'current-password', :autofocus => true }, label: t('challenge.prompt'), required: true
= f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'off', :autofocus => true }, label: t('challenge.prompt'), required: true
.actions
= f.button :button, t('challenge.confirm'), type: :submit

View File

@ -8,9 +8,9 @@
= f.input :reset_password_token, as: :hidden
.fields-group
= f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'new-password', :minlength => User.password_length.first, :maxlength => User.password_length.last }, required: true
= f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, required: true
.fields-group
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'new-password' }, required: true
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true
.actions
= f.button :button, t('auth.set_new_password'), type: :submit

View File

@ -13,13 +13,13 @@
.fields-row__column.fields-group.fields-row__column-6
= f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, disabled: current_account.suspended?
.fields-row__column.fields-group.fields-row__column-6
= f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'current-password' }, required: true, disabled: current_account.suspended?, hint: false
= f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true, disabled: current_account.suspended?, hint: false
.fields-row
.fields-row__column.fields-group.fields-row__column-6
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'new-password', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended?
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended?
.fields-row__column.fields-group.fields-row__column-6
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'new-password' }, disabled: current_account.suspended?
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, disabled: current_account.suspended?
.actions
= f.button :button, t('generic.save_changes'), type: :submit, class: 'button', disabled: current_account.suspended?

View File

@ -12,7 +12,7 @@
- else
= f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false
.fields-group
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'current-password' }, hint: false
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false
.actions
= f.button :button, t('auth.login'), type: :submit

View File

@ -21,7 +21,7 @@
%hr.spacer/
- if current_user.encrypted_password.present?
= f.input :password, wrapper: :with_block_label, input_html: { :autocomplete => 'current-password' }, hint: t('deletes.confirm_password')
= f.input :password, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, hint: t('deletes.confirm_password')
- else
= f.input :username, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, hint: t('deletes.confirm_username')

View File

@ -19,7 +19,7 @@
.fields-row__column.fields-group.fields-row__column-6
- if current_user.encrypted_password.present?
= f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'current-password' }, required: true
= f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true
- else
= f.input :current_username, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true

View File

@ -48,7 +48,7 @@
.fields-row__column.fields-group.fields-row__column-6
- if current_user.encrypted_password.present?
= f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'current-password' }, required: true, disabled: on_cooldown?
= f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true, disabled: on_cooldown?
- else
= f.input :current_username, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true, disabled: on_cooldown?

View File

@ -1,95 +0,0 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service do
let(:collection_url) { 'https://example.com/account/tags' }
let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/account') }
let(:items) do
[
{ type: 'Hashtag', href: 'https://example.com/account/tagged/foo', name: 'Foo' },
{ type: 'Hashtag', href: 'https://example.com/account/tagged/bar', name: 'bar' },
{ type: 'Hashtag', href: 'https://example.com/account/tagged/baz', name: 'baZ' },
]
end
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
type: 'Collection',
id: collection_url,
items: items,
}.with_indifferent_access
end
subject { described_class.new }
shared_examples 'sets featured tags' do
before do
subject.call(actor, collection_url)
end
it 'sets expected tags as pinned tags' do
expect(actor.featured_tags.map(&:display_name)).to match_array ['Foo', 'bar', 'baZ']
end
end
describe '#call' do
context 'when the endpoint is a Collection' do
before do
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
end
it_behaves_like 'sets featured tags'
end
context 'when the account already has featured tags' do
before do
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
actor.featured_tags.create!(name: 'FoO')
actor.featured_tags.create!(name: 'baz')
actor.featured_tags.create!(name: 'oh').update(name: nil)
end
it_behaves_like 'sets featured tags'
end
context 'when the endpoint is an OrderedCollection' do
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
type: 'OrderedCollection',
id: collection_url,
orderedItems: items,
}.with_indifferent_access
end
before do
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
end
it_behaves_like 'sets featured tags'
end
context 'when the endpoint is a paginated Collection' do
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
type: 'Collection',
id: collection_url,
first: {
type: 'CollectionPage',
partOf: collection_url,
items: items,
}
}.with_indifferent_access
end
before do
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
end
it_behaves_like 'sets featured tags'
end
end
end