Merge pull request #229 from glitch-soc/glitch-theme
Advanced Next-Level Flavours And Skins For Mastodon™
This commit is contained in:
commit
81b0145759
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "app/javascript/themes/mastodon-go"]
|
||||
path = app/javascript/themes/mastodon-go
|
||||
url = https://github.com/marrus-sh/mastodon-go
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AboutController < ApplicationController
|
||||
before_action :set_pack
|
||||
before_action :set_body_classes
|
||||
before_action :set_instance_presenter, only: [:show, :more, :terms]
|
||||
|
||||
|
@ -21,6 +22,10 @@ class AboutController < ApplicationController
|
|||
|
||||
helper_method :new_user
|
||||
|
||||
def set_pack
|
||||
use_pack action_name == 'show' ? 'about' : 'common'
|
||||
end
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ class AccountsController < ApplicationController
|
|||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
use_pack 'public'
|
||||
@pinned_statuses = []
|
||||
|
||||
if current_account && @account.blocking?(current_account)
|
||||
|
|
|
@ -5,8 +5,13 @@ module Admin
|
|||
include Authorization
|
||||
include AccountableConcern
|
||||
|
||||
before_action :require_staff!
|
||||
|
||||
layout 'admin'
|
||||
|
||||
before_action :require_staff!
|
||||
before_action :set_pack
|
||||
|
||||
def set_pack
|
||||
use_pack 'admin'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,8 +12,8 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
helper_method :current_account
|
||||
helper_method :current_session
|
||||
helper_method :current_theme
|
||||
helper_method :theme_data
|
||||
helper_method :current_flavour
|
||||
helper_method :current_skin
|
||||
helper_method :single_user_mode?
|
||||
|
||||
rescue_from ActionController::RoutingError, with: :not_found
|
||||
|
@ -54,6 +54,73 @@ class ApplicationController < ActionController::Base
|
|||
new_user_session_path
|
||||
end
|
||||
|
||||
def pack(data, pack_name, skin = 'default')
|
||||
return nil unless pack?(data, pack_name)
|
||||
pack_data = {
|
||||
common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.flavour(current_flavour) : Themes.instance.core, 'common'),
|
||||
flavour: data['name'],
|
||||
pack: pack_name,
|
||||
preload: nil,
|
||||
skin: nil,
|
||||
}
|
||||
if data['pack'][pack_name].is_a?(Hash)
|
||||
pack_data[:common] = nil if data['pack'][pack_name]['use_common'] == false
|
||||
pack_data[:pack] = nil unless data['pack'][pack_name]['filename']
|
||||
if data['pack'][pack_name]['preload']
|
||||
pack_data[:preload] = [data['pack'][pack_name]['preload']] if data['pack'][pack_name]['preload'].is_a?(String)
|
||||
pack_data[:preload] = data['pack'][pack_name]['preload'] if data['pack'][pack_name]['preload'].is_a?(Array)
|
||||
end
|
||||
if skin != 'default' && data['skin'][skin]
|
||||
pack_data[:skin] = skin if data['skin'][skin].include?(pack_name)
|
||||
else # default skin
|
||||
pack_data[:skin] = 'default' if data['pack'][pack_name]['stylesheet']
|
||||
end
|
||||
end
|
||||
pack_data
|
||||
end
|
||||
|
||||
def pack?(data, pack_name)
|
||||
if data['pack'].is_a?(Hash) && data['pack'].key?(pack_name)
|
||||
return true if data['pack'][pack_name].is_a?(String) || data['pack'][pack_name].is_a?(Hash)
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def nil_pack(data, pack_name, skin = 'default')
|
||||
{
|
||||
common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.flavour(current_flavour) : Themes.instance.core, 'common', skin),
|
||||
flavour: data['name'],
|
||||
pack: nil,
|
||||
preload: nil,
|
||||
skin: nil,
|
||||
}
|
||||
end
|
||||
|
||||
def resolve_pack(data, pack_name, skin = 'default')
|
||||
result = pack(data, pack_name, skin)
|
||||
unless result
|
||||
if data['name'] && data.key?('fallback')
|
||||
if data['fallback'].nil?
|
||||
return nil_pack(data, pack_name, skin)
|
||||
elsif data['fallback'].is_a?(String) && Themes.instance.flavour(data['fallback'])
|
||||
return resolve_pack(Themes.instance.flavour(data['fallback']), pack_name, skin)
|
||||
elsif data['fallback'].is_a?(Array)
|
||||
data['fallback'].each do |fallback|
|
||||
return resolve_pack(Themes.instance.flavour(fallback), pack_name, skin) if Themes.instance.flavour(fallback)
|
||||
end
|
||||
end
|
||||
return nil_pack(data, pack_name, skin)
|
||||
end
|
||||
return data.key?('name') && data['name'] != Setting.default_settings['flavour'] ? resolve_pack(Themes.instance.flavour(Setting.default_settings['flavour']), pack_name, skin) : nil_pack(data, pack_name, skin)
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def use_pack(pack_name)
|
||||
@core = resolve_pack(Themes.instance.core, pack_name)
|
||||
@theme = resolve_pack(Themes.instance.flavour(current_flavour), pack_name, current_skin)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def forbidden
|
||||
|
@ -84,13 +151,14 @@ class ApplicationController < ActionController::Base
|
|||
@current_session ||= SessionActivation.find_by(session_id: cookies.signed['_session_id'])
|
||||
end
|
||||
|
||||
def current_theme
|
||||
return Setting.default_settings['theme'] unless Themes.instance.names.include? current_user&.setting_theme
|
||||
current_user.setting_theme
|
||||
def current_flavour
|
||||
return Setting.default_settings['flavour'] unless Themes.instance.flavours.include? current_user&.setting_flavour
|
||||
current_user.setting_flavour
|
||||
end
|
||||
|
||||
def theme_data
|
||||
Themes.instance.get(current_theme)
|
||||
def current_skin
|
||||
return 'default' unless Themes.instance.skins_for(current_flavour).include? current_user&.setting_skin
|
||||
current_user.setting_skin
|
||||
end
|
||||
|
||||
def cache_collection(raw, klass)
|
||||
|
|
|
@ -2,10 +2,17 @@
|
|||
|
||||
class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||
layout 'auth'
|
||||
before_action :set_pack
|
||||
|
||||
def show
|
||||
super do |user|
|
||||
BootstrapTimelineWorker.perform_async(user.account_id) if user.errors.empty?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_pack
|
||||
use_pack 'auth'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class Auth::PasswordsController < Devise::PasswordsController
|
||||
before_action :check_validity_of_reset_password_token, only: :edit
|
||||
before_action :set_pack
|
||||
|
||||
layout 'auth'
|
||||
|
||||
|
@ -17,4 +18,8 @@ class Auth::PasswordsController < Devise::PasswordsController
|
|||
def reset_password_token_is_valid?
|
||||
resource_class.with_reset_password_token(params[:reset_password_token]).present?
|
||||
end
|
||||
|
||||
def set_pack
|
||||
use_pack 'auth'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
|
||||
before_action :check_enabled_registrations, only: [:new, :create]
|
||||
before_action :configure_sign_up_params, only: [:create]
|
||||
before_action :set_pack
|
||||
before_action :set_sessions, only: [:edit, :update]
|
||||
before_action :set_instance_presenter, only: [:new, :create, :update]
|
||||
|
||||
|
@ -55,6 +56,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
|
||||
private
|
||||
|
||||
def set_pack
|
||||
use_pack %w(edit update).include?(action_name) ? 'admin' : 'auth'
|
||||
end
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
skip_before_action :check_suspension, only: [:destroy]
|
||||
prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
|
||||
before_action :set_instance_presenter, only: [:new]
|
||||
before_action :set_pack
|
||||
|
||||
def create
|
||||
super do |resource|
|
||||
|
@ -85,6 +86,10 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
private
|
||||
|
||||
def set_pack
|
||||
use_pack 'auth'
|
||||
end
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ class AuthorizeFollowsController < ApplicationController
|
|||
layout 'modal'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_pack
|
||||
|
||||
def show
|
||||
@account = located_account || render(:error)
|
||||
|
@ -23,6 +24,10 @@ class AuthorizeFollowsController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def set_pack
|
||||
use_pack 'modal'
|
||||
end
|
||||
|
||||
def follow_attempt
|
||||
FollowService.new.call(current_account, acct_without_prefix)
|
||||
end
|
||||
|
|
|
@ -7,7 +7,9 @@ class FollowerAccountsController < ApplicationController
|
|||
@follows = Follow.where(target_account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.html do
|
||||
use_pack 'public'
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: collection_presenter,
|
||||
|
|
|
@ -7,7 +7,9 @@ class FollowingAccountsController < ApplicationController
|
|||
@follows = Follow.where(account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.html do
|
||||
use_pack 'public'
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: collection_presenter,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class HomeController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_pack
|
||||
before_action :set_initial_state_json
|
||||
|
||||
def index
|
||||
|
@ -37,6 +38,10 @@ class HomeController < ApplicationController
|
|||
redirect_to(default_redirect_path)
|
||||
end
|
||||
|
||||
def set_pack
|
||||
use_pack 'home'
|
||||
end
|
||||
|
||||
def set_initial_state_json
|
||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||
@initial_state_json = serializable_resource.to_json
|
||||
|
|
|
@ -4,6 +4,7 @@ class RemoteFollowController < ApplicationController
|
|||
layout 'modal'
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_pack
|
||||
before_action :gone, if: :suspended_account?
|
||||
|
||||
def new
|
||||
|
@ -31,6 +32,10 @@ class RemoteFollowController < ApplicationController
|
|||
{ acct: session[:remote_follow] }
|
||||
end
|
||||
|
||||
def set_pack
|
||||
use_pack 'modal'
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username])
|
||||
end
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ApplicationsController < ApplicationController
|
||||
layout 'admin'
|
||||
class Settings::ApplicationsController < Settings::BaseController
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_application, only: [:show, :update, :destroy, :regenerate]
|
||||
before_action :prepare_scopes, only: [:create, :update]
|
||||
|
||||
|
|
12
app/controllers/settings/base_controller.rb
Normal file
12
app/controllers/settings/base_controller.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::BaseController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_pack
|
||||
|
||||
def set_pack
|
||||
use_pack 'settings'
|
||||
end
|
||||
end
|
|
@ -1,10 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::DeletesController < ApplicationController
|
||||
layout 'admin'
|
||||
class Settings::DeletesController < Settings::BaseController
|
||||
|
||||
before_action :check_enabled_deletion
|
||||
before_action :authenticate_user!
|
||||
prepend_before_action :check_enabled_deletion
|
||||
|
||||
def show
|
||||
@confirmation = Form::DeleteConfirmation.new
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ExportsController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
||||
class Settings::ExportsController < Settings::BaseController
|
||||
def show
|
||||
@export = Export.new(current_account)
|
||||
end
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
require 'sidekiq-bulk'
|
||||
|
||||
class Settings::FollowerDomainsController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
||||
class Settings::FollowerDomainsController < Settings::BaseController
|
||||
def show
|
||||
@account = current_account
|
||||
@domains = current_account.followers.reorder('MIN(follows.id) DESC').group('accounts.domain').select('accounts.domain, count(accounts.id) as accounts_from_domain').page(params[:page]).per(10)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ImportsController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
class Settings::ImportsController < Settings::BaseController
|
||||
before_action :set_account
|
||||
|
||||
def show
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::KeywordMutesController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
class Settings::KeywordMutesController < Settings::BaseController
|
||||
before_action :load_keyword_mute, only: [:edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::NotificationsController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
||||
class Settings::NotificationsController < Settings::BaseController
|
||||
def show; end
|
||||
|
||||
def update
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::PreferencesController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
||||
class Settings::PreferencesController < Settings::BaseController
|
||||
def show; end
|
||||
|
||||
def update
|
||||
|
@ -42,7 +38,8 @@ class Settings::PreferencesController < ApplicationController
|
|||
:setting_reduce_motion,
|
||||
:setting_system_font_ui,
|
||||
:setting_noindex,
|
||||
:setting_theme,
|
||||
:setting_flavour,
|
||||
:setting_skin,
|
||||
notification_emails: %i(follow follow_request reblog favourite mention digest),
|
||||
interactions: %i(must_be_follower must_be_following)
|
||||
)
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ProfilesController < ApplicationController
|
||||
class Settings::ProfilesController < Settings::BaseController
|
||||
include ObfuscateFilename
|
||||
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_account
|
||||
|
||||
obfuscate_filename [:account, :avatar]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Intentionally does not inherit from BaseController
|
||||
class Settings::SessionsController < ApplicationController
|
||||
before_action :set_session, only: :destroy
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Settings
|
||||
class TwoFactorAuthenticationsController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
class TwoFactorAuthenticationsController < BaseController
|
||||
before_action :verify_otp_required, only: [:create]
|
||||
|
||||
def show
|
||||
|
|
|
@ -4,6 +4,7 @@ class SharesController < ApplicationController
|
|||
layout 'modal'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_pack
|
||||
before_action :set_body_classes
|
||||
|
||||
def show
|
||||
|
@ -24,6 +25,10 @@ class SharesController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def set_pack
|
||||
use_pack 'share'
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'compose-standalone'
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ class StatusesController < ApplicationController
|
|||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
use_pack 'public'
|
||||
@ancestors = @status.reply? ? cache_collection(@status.ancestors(current_account), Status) : []
|
||||
@descendants = cache_collection(@status.descendants(current_account), Status)
|
||||
|
||||
|
@ -37,6 +38,7 @@ class StatusesController < ApplicationController
|
|||
end
|
||||
|
||||
def embed
|
||||
use_pack 'embed'
|
||||
response.headers['X-Frame-Options'] = 'ALLOWALL'
|
||||
render 'stream_entries/embed', layout: 'embedded'
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ class StreamEntriesController < ApplicationController
|
|||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
use_pack 'public'
|
||||
@ancestors = @stream_entry.activity.reply? ? cache_collection(@stream_entry.activity.ancestors(current_account), Status) : []
|
||||
@descendants = cache_collection(@stream_entry.activity.descendants(current_account), Status)
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ class TagsController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
use_pack 'about'
|
||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||
@initial_state_json = serializable_resource.to_json
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// This file will be loaded on admin pages, regardless of theme.
|
||||
|
||||
import { delegate } from 'rails-ujs';
|
||||
|
||||
function handleDeleteStatus(event) {
|
8
app/javascript/core/common.js
Normal file
8
app/javascript/core/common.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
// This file will be loaded on all pages, regardless of theme.
|
||||
|
||||
import { start } from 'rails-ujs';
|
||||
import 'font-awesome/css/font-awesome.css';
|
||||
|
||||
require.context('../images/', true);
|
||||
|
||||
start();
|
23
app/javascript/core/embed.js
Normal file
23
app/javascript/core/embed.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
// This file will be loaded on embed pages, regardless of theme.
|
||||
|
||||
window.addEventListener('message', e => {
|
||||
const data = e.data || {};
|
||||
|
||||
if (!window.parent || data.type !== 'setHeight') {
|
||||
return;
|
||||
}
|
||||
|
||||
function setEmbedHeight () {
|
||||
window.parent.postMessage({
|
||||
type: 'setHeight',
|
||||
id: data.id,
|
||||
height: document.getElementsByTagName('html')[0].scrollHeight,
|
||||
}, '*');
|
||||
};
|
||||
|
||||
if (['interactive', 'complete'].includes(document.readyState)) {
|
||||
setEmbedHeight();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', setEmbedHeight);
|
||||
}
|
||||
});
|
25
app/javascript/core/public.js
Normal file
25
app/javascript/core/public.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
// This file will be loaded on public pages, regardless of theme.
|
||||
|
||||
const { delegate } = require('rails-ujs');
|
||||
|
||||
delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
|
||||
if (button !== 0) {
|
||||
return true;
|
||||
}
|
||||
window.location.href = target.href;
|
||||
return false;
|
||||
});
|
||||
|
||||
delegate(document, '.status__content__spoiler-link', 'click', ({ target }) => {
|
||||
const contentEl = target.parentNode.parentNode.querySelector('.e-content');
|
||||
|
||||
if (contentEl.style.display === 'block') {
|
||||
contentEl.style.display = 'none';
|
||||
target.parentNode.style.marginBottom = 0;
|
||||
} else {
|
||||
contentEl.style.display = 'block';
|
||||
target.parentNode.style.marginBottom = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
43
app/javascript/core/settings.js
Normal file
43
app/javascript/core/settings.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
// This file will be loaded on settings pages, regardless of theme.
|
||||
|
||||
const { length } = require('stringz');
|
||||
const { delegate } = require('rails-ujs');
|
||||
|
||||
import { processBio } from 'flavours/glitch/util/bio_metadata';
|
||||
|
||||
delegate(document, '.account_display_name', 'input', ({ target }) => {
|
||||
const nameCounter = document.querySelector('.name-counter');
|
||||
|
||||
if (nameCounter) {
|
||||
nameCounter.textContent = 30 - length(target.value);
|
||||
}
|
||||
});
|
||||
|
||||
delegate(document, '.account_note', 'input', ({ target }) => {
|
||||
const noteCounter = document.querySelector('.note-counter');
|
||||
|
||||
if (noteCounter) {
|
||||
const noteWithoutMetadata = processBio(target.value).text;
|
||||
noteCounter.textContent = 500 - length(noteWithoutMetadata);
|
||||
}
|
||||
});
|
||||
|
||||
delegate(document, '#account_avatar', 'change', ({ target }) => {
|
||||
const avatar = document.querySelector('.card.compact .avatar img');
|
||||
const [file] = target.files || [];
|
||||
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
|
||||
|
||||
avatar.src = url;
|
||||
});
|
||||
|
||||
delegate(document, '#account_header', 'change', ({ target }) => {
|
||||
const header = document.querySelector('.card.compact');
|
||||
const [file] = target.files || [];
|
||||
const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
|
||||
|
||||
header.style.backgroundImage = `url(${url})`;
|
||||
});
|
||||
|
||||
delegate(document, '#user_setting_flavour, #user_setting_skin', 'change', ({ target }) => {
|
||||
target.form.submit();
|
||||
});
|
16
app/javascript/core/theme.yml
Normal file
16
app/javascript/core/theme.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
# These packs will be loaded on every appropriate page, regardless of
|
||||
# theme.
|
||||
pack:
|
||||
about:
|
||||
admin: admin.js
|
||||
auth:
|
||||
common:
|
||||
filename: common.js
|
||||
stylesheet: true
|
||||
embed: embed.js
|
||||
error:
|
||||
home:
|
||||
modal:
|
||||
public: public.js
|
||||
settings: settings.js
|
||||
share:
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
|
||||
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
|
||||
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
||||
export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
|
||||
export const STATUS_CARD_FETCH_REQUEST = 'STATUS_CARD_FETCH_REQUEST';
|
||||
export const STATUS_CARD_FETCH_SUCCESS = 'STATUS_CARD_FETCH_SUCCESS';
|
|
@ -1,6 +1,6 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import { throttle } from 'lodash';
|
||||
import { search as emojiSearch } from 'themes/glitch/util/emoji/emoji_mart_search_light';
|
||||
import { search as emojiSearch } from 'flavours/glitch/util/emoji/emoji_mart_search_light';
|
||||
import { useEmoji } from './emojis';
|
||||
|
||||
import {
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
|
||||
export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST';
|
||||
export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS';
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
|
||||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
||||
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
|
||||
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
|
||||
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
|
|
@ -1,6 +1,6 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { openModal } from 'themes/glitch/actions/modal';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
|
||||
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
|
||||
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import { fetchRelationships } from './accounts';
|
|
@ -1,10 +1,10 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
|
||||
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
|
||||
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
|
||||
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
|
||||
|
||||
import { me } from 'themes/glitch/util/initial_state';
|
||||
import { me } from 'flavours/glitch/util/initial_state';
|
||||
|
||||
export function fetchPinnedStatuses() {
|
||||
return (dispatch, getState) => {
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import { openModal, closeModal } from './modal';
|
||||
|
||||
export const REPORT_INIT = 'REPORT_INIT';
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
|
||||
export const SEARCH_CHANGE = 'SEARCH_CHANGE';
|
||||
export const SEARCH_CLEAR = 'SEARCH_CLEAR';
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'themes/glitch/util/api';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
import { fetchStatusCard } from './cards';
|
|
@ -1,4 +1,4 @@
|
|||
import { connectStream } from 'themes/glitch/util/stream';
|
||||
import { connectStream } from 'flavours/glitch/util/stream';
|
||||
import {
|
||||
updateTimeline,
|
||||
deleteFromTimelines,
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'themes/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
|
@ -7,7 +7,7 @@ import Permalink from './permalink';
|
|||
import IconButton from './icon_button';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { me } from 'themes/glitch/util/initial_state';
|
||||
import { me } from 'flavours/glitch/util/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import unicodeMapping from 'themes/glitch/util/emoji/emoji_unicode_mapping_light';
|
||||
import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
|
||||
|
||||
const assetHost = process.env.CDN_HOST || '';
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import AutosuggestAccountContainer from 'themes/glitch/features/compose/containers/autosuggest_account_container';
|
||||
import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
|
||||
import AutosuggestEmoji from './autosuggest_emoji';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isRtl } from 'themes/glitch/util/rtl';
|
||||
import { isRtl } from 'flavours/glitch/util/rtl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
import classNames from 'classnames';
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import Motion from 'themes/glitch/util/optional_motion';
|
||||
import Motion from 'flavours/glitch/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import PropTypes from 'prop-types';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
import { scrollTop } from 'themes/glitch/util/scroll';
|
||||
import { scrollTop } from 'flavours/glitch/util/scroll';
|
||||
|
||||
export default class Column extends React.PureComponent {
|
||||
|
|
@ -4,8 +4,7 @@ import classNames from 'classnames';
|
|||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
// Glitch imports
|
||||
import NotificationPurgeButtonsContainer from 'themes/glitch/containers/notification_purge_buttons_container';
|
||||
import NotificationPurgeButtonsContainer from 'flavours/glitch/containers/notification_purge_buttons_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import IconButton from './icon_button';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Motion from 'themes/glitch/util/optional_motion';
|
||||
import Motion from 'flavours/glitch/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import Motion from 'themes/glitch/util/optional_motion';
|
||||
import Motion from 'flavours/glitch/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import scheduleIdleTask from 'themes/glitch/util/schedule_idle_task';
|
||||
import getRectFromEntry from 'themes/glitch/util/get_rect_from_entry';
|
||||
import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task';
|
||||
import getRectFromEntry from 'flavours/glitch/util/get_rect_from_entry';
|
||||
import { is } from 'immutable';
|
||||
|
||||
// Diff these props in the "rendered" state
|
|
@ -4,9 +4,9 @@ import PropTypes from 'prop-types';
|
|||
import { is } from 'immutable';
|
||||
import IconButton from './icon_button';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { isIOS } from 'themes/glitch/util/is_mobile';
|
||||
import { isIOS } from 'flavours/glitch/util/is_mobile';
|
||||
import classNames from 'classnames';
|
||||
import { autoPlayGif } from 'themes/glitch/util/initial_state';
|
||||
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
|
@ -1,13 +1,13 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { ScrollContainer } from 'react-router-scroll-4';
|
||||
import PropTypes from 'prop-types';
|
||||
import IntersectionObserverArticleContainer from 'themes/glitch/containers/intersection_observer_article_container';
|
||||
import IntersectionObserverArticleContainer from 'flavours/glitch/containers/intersection_observer_article_container';
|
||||
import LoadMore from './load_more';
|
||||
import IntersectionObserverWrapper from 'themes/glitch/util/intersection_observer_wrapper';
|
||||
import IntersectionObserverWrapper from 'flavours/glitch/util/intersection_observer_wrapper';
|
||||
import { throttle } from 'lodash';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import classNames from 'classnames';
|
||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'themes/glitch/util/fullscreen';
|
||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/util/fullscreen';
|
||||
|
||||
export default class ScrollableList extends PureComponent {
|
||||
|
|
@ -6,9 +6,9 @@ import StatusHeader from './status_header';
|
|||
import StatusContent from './status_content';
|
||||
import StatusActionBar from './status_action_bar';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { MediaGallery, Video } from 'themes/glitch/util/async-components';
|
||||
import { MediaGallery, Video } from 'flavours/glitch/util/async-components';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import NotificationOverlayContainer from 'themes/glitch/features/notifications/containers/overlay_container';
|
||||
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
|
||||
import classNames from 'classnames';
|
||||
|
||||
// We use the component (and not the container) since we do not want
|
|
@ -1,14 +1,11 @@
|
|||
// THIS FILE EXISTS FOR UPSTREAM COMPATIBILITY & SHOULDN'T BE USED !!
|
||||
// SEE INSTEAD : glitch/components/status/action_bar
|
||||
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import IconButton from './icon_button';
|
||||
import DropdownMenuContainer from 'themes/glitch/containers/dropdown_menu_container';
|
||||
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { me } from 'themes/glitch/util/initial_state';
|
||||
import { me } from 'flavours/glitch/util/initial_state';
|
||||
import RelativeTimestamp from './relative_timestamp';
|
||||
|
||||
const messages = defineMessages({
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isRtl } from 'themes/glitch/util/rtl';
|
||||
import { isRtl } from 'flavours/glitch/util/rtl';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Permalink from './permalink';
|
||||
import classnames from 'classnames';
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import StatusContainer from 'themes/glitch/containers/status_container';
|
||||
import StatusContainer from 'flavours/glitch/containers/status_container';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ScrollableList from './scrollable_list';
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { makeGetAccount } from 'themes/glitch/selectors';
|
||||
import Account from 'themes/glitch/components/account';
|
||||
import { makeGetAccount } from 'flavours/glitch/selectors';
|
||||
import Account from 'flavours/glitch/components/account';
|
||||
import {
|
||||
followAccount,
|
||||
unfollowAccount,
|
||||
|
@ -10,10 +10,10 @@ import {
|
|||
unblockAccount,
|
||||
muteAccount,
|
||||
unmuteAccount,
|
||||
} from 'themes/glitch/actions/accounts';
|
||||
import { openModal } from 'themes/glitch/actions/modal';
|
||||
import { initMuteModal } from 'themes/glitch/actions/mutes';
|
||||
import { unfollowModal } from 'themes/glitch/util/initial_state';
|
||||
} from 'flavours/glitch/actions/accounts';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { initMuteModal } from 'flavours/glitch/actions/mutes';
|
||||
import { unfollowModal } from 'flavours/glitch/util/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Card from 'themes/glitch/features/status/components/card';
|
||||
import Card from 'flavours/glitch/features/status/components/card';
|
||||
import { fromJS } from 'immutable';
|
||||
|
||||
export default class CardContainer extends React.PureComponent {
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import configureStore from 'themes/glitch/store/configureStore';
|
||||
import { hydrateStore } from 'themes/glitch/actions/store';
|
||||
import configureStore from 'flavours/glitch/store/configureStore';
|
||||
import { hydrateStore } from 'flavours/glitch/actions/store';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import { getLocale } from 'mastodon/locales';
|
||||
import Compose from 'themes/glitch/features/standalone/compose';
|
||||
import initialState from 'themes/glitch/util/initial_state';
|
||||
import Compose from 'flavours/glitch/features/standalone/compose';
|
||||
import initialState from 'flavours/glitch/util/initial_state';
|
||||
|
||||
const { localeData, messages } = getLocale();
|
||||
addLocaleData(localeData);
|
|
@ -1,7 +1,7 @@
|
|||
import { openModal, closeModal } from 'themes/glitch/actions/modal';
|
||||
import { openModal, closeModal } from 'flavours/glitch/actions/modal';
|
||||
import { connect } from 'react-redux';
|
||||
import DropdownMenu from 'themes/glitch/components/dropdown_menu';
|
||||
import { isUserTouching } from 'themes/glitch/util/is_mobile';
|
||||
import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
||||
import { isUserTouching } from 'flavours/glitch/util/is_mobile';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isModalOpen: state.get('modal').modalType === 'ACTIONS',
|
|
@ -1,6 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import IntersectionObserverArticle from 'themes/glitch/components/intersection_observer_article';
|
||||
import { setHeight } from 'themes/glitch/actions/height_cache';
|
||||
import IntersectionObserverArticle from 'flavours/glitch/components/intersection_observer_article';
|
||||
import { setHeight } from 'flavours/glitch/actions/height_cache';
|
||||
|
||||
const makeMapStateToProps = (state, props) => ({
|
||||
cachedHeight: state.getIn(['height_cache', props.saveHeightKey, props.id]),
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue