diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb
index 47690e81eb..8785df14ed 100644
--- a/app/controllers/about_controller.rb
+++ b/app/controllers/about_controller.rb
@@ -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
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 75915b3371..309cb65daa 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -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)
diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb
index db4839a8f7..7261345090 100644
--- a/app/controllers/admin/base_controller.rb
+++ b/app/controllers/admin/base_controller.rb
@@ -4,8 +4,13 @@ module Admin
class BaseController < ApplicationController
include Authorization
- before_action :require_staff!
-
layout 'admin'
+
+ before_action :require_staff!
+ before_action :set_pack
+
+ def set_pack
+ use_pack 'admin'
+ end
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f5dbe837e4..7cc4eea275 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -12,8 +12,6 @@ class ApplicationController < ActionController::Base
helper_method :current_account
helper_method :current_session
- helper_method :current_theme
- helper_method :theme_data
helper_method :single_user_mode?
rescue_from ActionController::RoutingError, with: :not_found
@@ -54,6 +52,69 @@ class ApplicationController < ActionController::Base
new_user_session_path
end
+ def pack(data, pack_name)
+ return nil unless pack?(data, pack_name)
+ pack_data = {
+ common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common'),
+ name: data['name'],
+ pack: pack_name,
+ preload: nil,
+ stylesheet: false
+ }
+ 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
+ pack_data[:stylesheet] = true if data['pack'][pack_name]['stylesheet']
+ 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)
+ {
+ common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common'),
+ name: data['name'],
+ pack: nil,
+ preload: nil,
+ stylesheet: false
+ }
+ end
+
+ def resolve_pack(data, pack_name)
+ result = pack(data, pack_name)
+ unless result
+ if data['name'] && data.key?('fallback')
+ if data['fallback'].nil?
+ return nil_pack(data, pack_name)
+ elsif data['fallback'].is_a?(String) && Themes.instance.get(data['fallback'])
+ return resolve_pack(Themes.instance.get(data['fallback']), pack_name)
+ elsif data['fallback'].is_a?(Array)
+ data['fallback'].each do |fallback|
+ return resolve_pack(Themes.instance.get(fallback), pack_name) if Themes.instance.get(fallback)
+ end
+ end
+ return nil_pack(data, pack_name)
+ end
+ return data.key?('name') && data['name'] != default_theme ? resolve_pack(Themes.instance.get(default_theme), pack_name) : nil_pack(data, pack_name)
+ end
+ result
+ end
+
+ def use_pack(pack_name)
+ @core = resolve_pack(Themes.instance.core, pack_name)
+ @theme = resolve_pack(Themes.instance.get(current_theme), pack_name)
+ end
+
protected
def forbidden
@@ -84,13 +145,13 @@ 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 default_theme
+ Setting.default_settings['theme']
end
- def theme_data
- Themes.instance.get(current_theme)
+ def current_theme
+ return default_theme unless Themes.instance.names.include? current_user&.setting_theme
+ current_user.setting_theme
end
def cache_collection(raw, klass)
diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb
index d5e8e58ede..5ffa1c9a30 100644
--- a/app/controllers/auth/confirmations_controller.rb
+++ b/app/controllers/auth/confirmations_controller.rb
@@ -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
diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb
index 171b997dc4..e0400aa3df 100644
--- a/app/controllers/auth/passwords_controller.rb
+++ b/app/controllers/auth/passwords_controller.rb
@@ -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
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
index 223db96ff2..cc7a69ab07 100644
--- a/app/controllers/auth/registrations_controller.rb
+++ b/app/controllers/auth/registrations_controller.rb
@@ -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_path
before_action :set_sessions, only: [:edit, :update]
before_action :set_instance_presenter, only: [:new, :create, :update]
@@ -40,6 +41,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
diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb
index a5acb6c36f..72d5441022 100644
--- a/app/controllers/auth/sessions_controller.rb
+++ b/app/controllers/auth/sessions_controller.rb
@@ -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
diff --git a/app/controllers/authorize_follows_controller.rb b/app/controllers/authorize_follows_controller.rb
index 78b5641836..2d29bd3794 100644
--- a/app/controllers/authorize_follows_controller.rb
+++ b/app/controllers/authorize_follows_controller.rb
@@ -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
diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb
index 399e79665e..080cbde114 100644
--- a/app/controllers/follower_accounts_controller.rb
+++ b/app/controllers/follower_accounts_controller.rb
@@ -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,
diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb
index 1e73d4bd40..74e83ad814 100644
--- a/app/controllers/following_accounts_controller.rb
+++ b/app/controllers/following_accounts_controller.rb
@@ -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,
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index 21dde20ce4..7437a647e1 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -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
diff --git a/app/controllers/remote_follow_controller.rb b/app/controllers/remote_follow_controller.rb
index 48b026aa5a..e6f3798861 100644
--- a/app/controllers/remote_follow_controller.rb
+++ b/app/controllers/remote_follow_controller.rb
@@ -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
diff --git a/app/controllers/settings/applications_controller.rb b/app/controllers/settings/applications_controller.rb
index 8fc9a0fa99..35a6f7f9e7 100644
--- a/app/controllers/settings/applications_controller.rb
+++ b/app/controllers/settings/applications_controller.rb
@@ -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]
diff --git a/app/controllers/settings/base_controller.rb b/app/controllers/settings/base_controller.rb
new file mode 100644
index 0000000000..7322d461be
--- /dev/null
+++ b/app/controllers/settings/base_controller.rb
@@ -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
diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb
index 80002b995e..e4cb35a8e9 100644
--- a/app/controllers/settings/deletes_controller.rb
+++ b/app/controllers/settings/deletes_controller.rb
@@ -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!
def show
@confirmation = Form::DeleteConfirmation.new
diff --git a/app/controllers/settings/exports_controller.rb b/app/controllers/settings/exports_controller.rb
index ae62f00c1e..9c03ece860 100644
--- a/app/controllers/settings/exports_controller.rb
+++ b/app/controllers/settings/exports_controller.rb
@@ -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
diff --git a/app/controllers/settings/follower_domains_controller.rb b/app/controllers/settings/follower_domains_controller.rb
index 9968504e5f..141b2270dd 100644
--- a/app/controllers/settings/follower_domains_controller.rb
+++ b/app/controllers/settings/follower_domains_controller.rb
@@ -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)
diff --git a/app/controllers/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb
index 0db13d1ca6..dbd136ebe1 100644
--- a/app/controllers/settings/imports_controller.rb
+++ b/app/controllers/settings/imports_controller.rb
@@ -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
diff --git a/app/controllers/settings/keyword_mutes_controller.rb b/app/controllers/settings/keyword_mutes_controller.rb
index f79e1b320b..699b8a3ef1 100644
--- a/app/controllers/settings/keyword_mutes_controller.rb
+++ b/app/controllers/settings/keyword_mutes_controller.rb
@@ -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
diff --git a/app/controllers/settings/notifications_controller.rb b/app/controllers/settings/notifications_controller.rb
index ce2530c541..6286e3ebf1 100644
--- a/app/controllers/settings/notifications_controller.rb
+++ b/app/controllers/settings/notifications_controller.rb
@@ -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
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index 0690267151..3aefd90a2d 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -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
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index 28f78a4fb8..dadc3d9118 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -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]
diff --git a/app/controllers/settings/sessions_controller.rb b/app/controllers/settings/sessions_controller.rb
index 0da1b027b8..780ea64b40 100644
--- a/app/controllers/settings/sessions_controller.rb
+++ b/app/controllers/settings/sessions_controller.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+# Intentionally does not inherit from BaseController
class Settings::SessionsController < ApplicationController
before_action :set_session, only: :destroy
diff --git a/app/controllers/settings/two_factor_authentications_controller.rb b/app/controllers/settings/two_factor_authentications_controller.rb
index 863cc7351b..8c7737e9dc 100644
--- a/app/controllers/settings/two_factor_authentications_controller.rb
+++ b/app/controllers/settings/two_factor_authentications_controller.rb
@@ -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
diff --git a/app/controllers/shares_controller.rb b/app/controllers/shares_controller.rb
index 994742c3df..81d279c8b4 100644
--- a/app/controllers/shares_controller.rb
+++ b/app/controllers/shares_controller.rb
@@ -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
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index e8a360fb57..84c9e7685e 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -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
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
index 5f61e2182c..b597ba4bbe 100644
--- a/app/controllers/stream_entries_controller.rb
+++ b/app/controllers/stream_entries_controller.rb
@@ -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
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 9f3090e37b..5d11a81390 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -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
diff --git a/app/javascript/core/about.js b/app/javascript/core/about.js
deleted file mode 100644
index 6ed0e4ad38..0000000000
--- a/app/javascript/core/about.js
+++ /dev/null
@@ -1 +0,0 @@
-// This file will be loaded on about pages, regardless of theme.
diff --git a/app/javascript/core/embed.js b/app/javascript/core/embed.js
index 8167706a35..6146e65929 100644
--- a/app/javascript/core/embed.js
+++ b/app/javascript/core/embed.js
@@ -13,7 +13,7 @@ window.addEventListener('message', e => {
id: data.id,
height: document.getElementsByTagName('html')[0].scrollHeight,
}, '*');
- });
+ };
if (['interactive', 'complete'].includes(document.readyState)) {
setEmbedHeight();
diff --git a/app/javascript/core/home.js b/app/javascript/core/home.js
deleted file mode 100644
index 3c2e015909..0000000000
--- a/app/javascript/core/home.js
+++ /dev/null
@@ -1 +0,0 @@
-// This file will be loaded on home pages, regardless of theme.
diff --git a/app/javascript/core/public.js b/app/javascript/core/public.js
index 1a36b7a5fd..47c34a2590 100644
--- a/app/javascript/core/public.js
+++ b/app/javascript/core/public.js
@@ -1 +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;
+});
diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js
index 91332ed5a5..7fb1d8e77a 100644
--- a/app/javascript/core/settings.js
+++ b/app/javascript/core/settings.js
@@ -1,65 +1,37 @@
// This file will be loaded on settings pages, regardless of theme.
-function main() {
- const { length } = require('stringz');
- const { delegate } = require('rails-ujs');
+const { length } = require('stringz');
+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, '.account_display_name', 'input', ({ target }) => {
+ const nameCounter = document.querySelector('.name-counter');
- 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;
- });
-
- 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})`;
- });
-}
-
-loadPolyfills().then(main).catch(error => {
- console.error(error);
+ 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})`;
});
diff --git a/app/javascript/core/share.js b/app/javascript/core/share.js
deleted file mode 100644
index 98a413632b..0000000000
--- a/app/javascript/core/share.js
+++ /dev/null
@@ -1 +0,0 @@
-// This file will be loaded on share pages, regardless of theme.
diff --git a/app/javascript/core/theme.yml b/app/javascript/core/theme.yml
new file mode 100644
index 0000000000..17e8e66b32
--- /dev/null
+++ b/app/javascript/core/theme.yml
@@ -0,0 +1,14 @@
+# These packs will be loaded on every appropriate page, regardless of
+# theme.
+pack:
+ about:
+ admin: admin.js
+ auth:
+ common: common.js
+ embed: embed.js
+ error:
+ home:
+ modal:
+ public: public.js
+ settings: settings.js
+ share:
diff --git a/app/javascript/locales/index.js b/app/javascript/locales/index.js
new file mode 100644
index 0000000000..421cb7fab0
--- /dev/null
+++ b/app/javascript/locales/index.js
@@ -0,0 +1,9 @@
+let theLocale;
+
+export function setLocale(locale) {
+ theLocale = locale;
+}
+
+export function getLocale() {
+ return theLocale;
+}
diff --git a/app/javascript/mastodon/locales/index.js b/app/javascript/mastodon/locales/index.js
index 421cb7fab0..7e72975618 100644
--- a/app/javascript/mastodon/locales/index.js
+++ b/app/javascript/mastodon/locales/index.js
@@ -1,9 +1 @@
-let theLocale;
-
-export function setLocale(locale) {
- theLocale = locale;
-}
-
-export function getLocale() {
- return theLocale;
-}
+export * from 'locales';
diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js
new file mode 100644
index 0000000000..63e12da42c
--- /dev/null
+++ b/app/javascript/packs/about.js
@@ -0,0 +1,22 @@
+import loadPolyfills from '../mastodon/load_polyfills';
+
+function loaded() {
+ const TimelineContainer = require('../mastodon/containers/timeline_container').default;
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+ const mountNode = document.getElementById('mastodon-timeline');
+
+ if (mountNode !== null) {
+ const props = JSON.parse(mountNode.getAttribute('data-props'));
+ ReactDOM.render(, mountNode);
+ }
+}
+
+function main() {
+ const ready = require('../mastodon/ready').default;
+ ready(loaded);
+}
+
+loadPolyfills().then(main).catch(error => {
+ console.error(error);
+});
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index ee5bf244c2..116632dea7 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -1,15 +1,5 @@
-// THIS IS THE `vanilla` THEME PACK FILE!!
-// IT'S HERE FOR UPSTREAM COMPATIBILITY!!
-// THE `glitch` PACK FILE IS IN `themes/glitch/index.js`!!
-
import loadPolyfills from '../mastodon/load_polyfills';
-// import default stylesheet with variables
-import 'font-awesome/css/font-awesome.css';
-import '../styles/application.scss';
-
-require.context('../images/', true);
-
loadPolyfills().then(() => {
require('../mastodon/main').default();
}).catch(e => {
diff --git a/app/javascript/packs/common.js b/app/javascript/packs/common.js
new file mode 100644
index 0000000000..f3156c1c66
--- /dev/null
+++ b/app/javascript/packs/common.js
@@ -0,0 +1,3 @@
+import 'font-awesome/css/font-awesome.css';
+import 'styles/application.scss'
+require.context('../images/', true);
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
new file mode 100644
index 0000000000..3472af6c13
--- /dev/null
+++ b/app/javascript/packs/public.js
@@ -0,0 +1,75 @@
+import loadPolyfills from '../mastodon/load_polyfills';
+import ready from '../mastodon/ready';
+
+function main() {
+ const IntlRelativeFormat = require('intl-relativeformat').default;
+ const emojify = require('../mastodon/features/emoji/emoji').default;
+ const { getLocale } = require('../mastodon/locales');
+ const { localeData } = getLocale();
+ const VideoContainer = require('../mastodon/containers/video_container').default;
+ const MediaGalleryContainer = require('../mastodon/containers/media_gallery_container').default;
+ const CardContainer = require('../mastodon/containers/card_container').default;
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+
+ localeData.forEach(IntlRelativeFormat.__addLocaleData);
+
+ ready(() => {
+ const locale = document.documentElement.lang;
+
+ const dateTimeFormat = new Intl.DateTimeFormat(locale, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ });
+
+ const relativeFormat = new IntlRelativeFormat(locale);
+
+ [].forEach.call(document.querySelectorAll('.emojify'), (content) => {
+ content.innerHTML = emojify(content.innerHTML);
+ });
+
+ [].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
+ const datetime = new Date(content.getAttribute('datetime'));
+ const formattedDate = dateTimeFormat.format(datetime);
+
+ content.title = formattedDate;
+ content.textContent = formattedDate;
+ });
+
+ [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
+ const datetime = new Date(content.getAttribute('datetime'));
+
+ content.title = dateTimeFormat.format(datetime);
+ content.textContent = relativeFormat.format(datetime);
+ });
+
+ [].forEach.call(document.querySelectorAll('.logo-button'), (content) => {
+ content.addEventListener('click', (e) => {
+ e.preventDefault();
+ window.open(e.target.href, 'mastodon-intent', 'width=400,height=400,resizable=no,menubar=no,status=no,scrollbars=yes');
+ });
+ });
+
+ [].forEach.call(document.querySelectorAll('[data-component="Video"]'), (content) => {
+ const props = JSON.parse(content.getAttribute('data-props'));
+ ReactDOM.render(, content);
+ });
+
+ [].forEach.call(document.querySelectorAll('[data-component="MediaGallery"]'), (content) => {
+ const props = JSON.parse(content.getAttribute('data-props'));
+ ReactDOM.render(, content);
+ });
+
+ [].forEach.call(document.querySelectorAll('[data-component="Card"]'), (content) => {
+ const props = JSON.parse(content.getAttribute('data-props'));
+ ReactDOM.render(, content);
+ });
+ });
+}
+
+loadPolyfills().then(main).catch(error => {
+ console.error(error);
+});
diff --git a/app/javascript/packs/share.js b/app/javascript/packs/share.js
new file mode 100644
index 0000000000..e9580f648b
--- /dev/null
+++ b/app/javascript/packs/share.js
@@ -0,0 +1,22 @@
+import loadPolyfills from '../mastodon/load_polyfills';
+
+function loaded() {
+ const ComposeContainer = require('../mastodon/containers/compose_container').default;
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+ const mountNode = document.getElementById('mastodon-compose');
+
+ if (mountNode !== null) {
+ const props = JSON.parse(mountNode.getAttribute('data-props'));
+ ReactDOM.render(, mountNode);
+ }
+}
+
+function main() {
+ const ready = require('../mastodon/ready').default;
+ ready(loaded);
+}
+
+loadPolyfills().then(main).catch(error => {
+ console.error(error);
+});
diff --git a/app/javascript/styles/common.scss b/app/javascript/styles/common.scss
deleted file mode 100644
index c1772e7ae1..0000000000
--- a/app/javascript/styles/common.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-// This makes our fonts available everywhere.
-
-@import 'fonts/roboto';
-@import 'fonts/roboto-mono';
-@import 'fonts/montserrat';
diff --git a/app/javascript/styles/win95.scss b/app/javascript/styles/win95.scss
index 885837b530..6c89fc5bf6 100644
--- a/app/javascript/styles/win95.scss
+++ b/app/javascript/styles/win95.scss
@@ -1,3 +1,8 @@
+// win95 theme from cybrespace.
+
+// Modified to inherit glitch styles (themes/glitch/styles/index.scss)
+// instead of vanilla ones (./application.scss)
+
$win95-bg: #bfbfbf;
$win95-dark-grey: #404040;
$win95-mid-grey: #808080;
@@ -17,7 +22,7 @@ $ui-highlight-color: $win95-window-header;
}
@mixin win95-outset() {
- box-shadow: inset -1px -1px 0px #000000,
+ box-shadow: inset -1px -1px 0px #000000,
inset 1px 1px 0px #ffffff,
inset -2px -2px 0px #808080,
inset 2px 2px 0px #dfdfdf;
@@ -41,7 +46,7 @@ $ui-highlight-color: $win95-window-header;
}
@mixin win95-inset() {
- box-shadow: inset 1px 1px 0px #000000,
+ box-shadow: inset 1px 1px 0px #000000,
inset -1px -1px 0px #ffffff,
inset 2px 2px 0px #808080,
inset -2px -2px 0px #dfdfdf;
@@ -51,7 +56,7 @@ $ui-highlight-color: $win95-window-header;
@mixin win95-tab() {
- box-shadow: inset -1px 0px 0px #000000,
+ box-shadow: inset -1px 0px 0px #000000,
inset 1px 0px 0px #ffffff,
inset 0px 1px 0px #ffffff,
inset 0px 2px 0px #dfdfdf,
@@ -71,7 +76,7 @@ $ui-highlight-color: $win95-window-header;
src: url('../fonts/premillenium/MSSansSerif.ttf') format('truetype');
}
-@import 'application';
+@import '../themes/glitch/styles/index'; // Imports glitch themes
/* borrowed from cybrespace style: wider columns and full column width images */
@@ -174,7 +179,7 @@ body.admin {
font-size:0px;
color:$win95-bg;
- background-image: url("../images/start.png");
+ background-image: url("../images/start.png");
background-repeat:no-repeat;
background-position:8%;
background-clip:padding-box;
@@ -336,7 +341,7 @@ body.admin {
border-radius:0px;
background-color:white;
@include win95-border-inset();
-
+
width:12px;
height:12px;
}
@@ -515,9 +520,9 @@ body.admin {
color:black;
font-weight:bold;
}
-.account__avatar,
-.account__avatar-overlay-base,
-.account__header__avatar,
+.account__avatar,
+.account__avatar-overlay-base,
+.account__header__avatar,
.account__avatar-overlay-overlay {
@include win95-border-slight-inset();
clip-path:none;
@@ -627,7 +632,7 @@ body.admin {
}
.status-card__description {
- color:black;
+ color:black;
}
.account__display-name strong, .status__display-name strong {
@@ -710,8 +715,8 @@ body.admin {
width:40px;
font-size:0px;
color:$win95-bg;
-
- background-image: url("../images/start.png");
+
+ background-image: url("../images/start.png");
background-repeat:no-repeat;
background-position:8%;
background-clip:padding-box;
@@ -723,7 +728,7 @@ body.admin {
}
.drawer__header a:first-child:hover {
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII=");
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII=");
background-repeat:no-repeat;
background-position:8%;
background-clip:padding-box;
@@ -732,7 +737,7 @@ body.admin {
}
.drawer__tab:first-child {
-
+
}
.search {
@@ -844,7 +849,7 @@ body.admin {
padding:4px 8px;
}
-.privacy-dropdown.active
+.privacy-dropdown.active
.privacy-dropdown__value {
background: $win95-bg;
box-shadow:unset;
@@ -935,7 +940,7 @@ body.admin {
background-color:$win95-bg;
border:1px solid black;
box-sizing:content-box;
-
+
}
.emoji-dialog .emoji-search {
@@ -1010,8 +1015,8 @@ body.admin {
width:60px;
font-size:0px;
color:$win95-bg;
-
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII=");
+
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII=");
background-repeat:no-repeat;
background-position:8%;
background-clip:padding-box;
@@ -1049,40 +1054,40 @@ body.admin {
}
}
-.column-link[href="/web/timelines/public"] {
- background-image: url("../images/icon_public.png");
+.column-link[href="/web/timelines/public"] {
+ background-image: url("../images/icon_public.png");
&:hover { background-image: url("../images/icon_public.png"); }
}
-.column-link[href="/web/timelines/public/local"] {
- background-image: url("../images/icon_local.png");
+.column-link[href="/web/timelines/public/local"] {
+ background-image: url("../images/icon_local.png");
&:hover { background-image: url("../images/icon_local.png"); }
}
-.column-link[href="/web/pinned"] {
- background-image: url("../images/icon_pin.png");
+.column-link[href="/web/pinned"] {
+ background-image: url("../images/icon_pin.png");
&:hover { background-image: url("../images/icon_pin.png"); }
}
-.column-link[href="/web/favourites"] {
- background-image: url("../images/icon_likes.png");
+.column-link[href="/web/favourites"] {
+ background-image: url("../images/icon_likes.png");
&:hover { background-image: url("../images/icon_likes.png"); }
}
-.column-link[href="/web/blocks"] {
- background-image: url("../images/icon_blocks.png");
+.column-link[href="/web/blocks"] {
+ background-image: url("../images/icon_blocks.png");
&:hover { background-image: url("../images/icon_blocks.png"); }
}
-.column-link[href="/web/mutes"] {
- background-image: url("../images/icon_mutes.png");
+.column-link[href="/web/mutes"] {
+ background-image: url("../images/icon_mutes.png");
&:hover { background-image: url("../images/icon_mutes.png"); }
}
-.column-link[href="/settings/preferences"] {
- background-image: url("../images/icon_settings.png");
+.column-link[href="/settings/preferences"] {
+ background-image: url("../images/icon_settings.png");
&:hover { background-image: url("../images/icon_settings.png"); }
}
-.column-link[href="/about/more"] {
- background-image: url("../images/icon_about.png");
+.column-link[href="/about/more"] {
+ background-image: url("../images/icon_about.png");
&:hover { background-image: url("../images/icon_about.png"); }
}
-.column-link[href="/auth/sign_out"] {
- background-image: url("../images/icon_logout.png");
+.column-link[href="/auth/sign_out"] {
+ background-image: url("../images/icon_logout.png");
&:hover { background-image: url("../images/icon_logout.png"); }
}
@@ -1098,7 +1103,7 @@ body.admin {
line-height:30px;
padding-left:20px;
padding-right:40px;
-
+
left:0px;
bottom:-30px;
display:block;
@@ -1106,9 +1111,9 @@ body.admin {
background-color:#7f7f7f;
width:200%;
height:30px;
-
+
-ms-transform: rotate(-90deg);
-
+
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
transform-origin:top left;
@@ -1189,7 +1194,7 @@ body.admin {
left:unset;
}
-.dropdown > .icon-button, .detailed-status__button > .icon-button,
+.dropdown > .icon-button, .detailed-status__button > .icon-button,
.status__action-bar > .icon-button, .star-icon i {
/* i don't know what's going on with the inline
styles someone should look at the react code */
@@ -1239,8 +1244,8 @@ body.admin {
background:$win95-bg;
}
-.actions-modal::before,
-.boost-modal::before,
+.actions-modal::before,
+.boost-modal::before,
.confirmation-modal::before,
.report-modal::before {
content: "Confirmation";
@@ -1278,8 +1283,8 @@ body.admin {
.confirmation-modal__cancel-button {
color:black;
- &:active,
- &:focus,
+ &:active,
+ &:focus,
&:hover {
color:black;
}
@@ -1566,10 +1571,10 @@ a.table-action-link:hover,
background-color:white;
}
-.simple_form input[type=text],
-.simple_form input[type=number],
-.simple_form input[type=email],
-.simple_form input[type=password],
+.simple_form input[type=text],
+.simple_form input[type=number],
+.simple_form input[type=email],
+.simple_form input[type=password],
.simple_form textarea {
color:black;
background-color:white;
@@ -1580,8 +1585,8 @@ a.table-action-link:hover,
}
}
-.simple_form button,
-.simple_form .button,
+.simple_form button,
+.simple_form .button,
.simple_form .block-button
{
background: $win95-bg;
@@ -1608,8 +1613,8 @@ a.table-action-link:hover,
}
}
-.simple_form button.negative,
-.simple_form .button.negative,
+.simple_form button.negative,
+.simple_form .button.negative,
.simple_form .block-button.negative
{
background: $win95-bg;
@@ -1631,8 +1636,8 @@ a.table-action-link:hover,
border-right-color:#f5f5f5;
width:12px;
height:12px;
- display:inline-block;
- vertical-align:middle;
+ display:inline-block;
+ vertical-align:middle;
margin-right:2px;
}
diff --git a/app/javascript/themes/glitch/containers/mastodon.js b/app/javascript/themes/glitch/containers/mastodon.js
index 3484706378..755b5564a1 100644
--- a/app/javascript/themes/glitch/containers/mastodon.js
+++ b/app/javascript/themes/glitch/containers/mastodon.js
@@ -9,7 +9,7 @@ import UI from 'themes/glitch/features/ui';
import { hydrateStore } from 'themes/glitch/actions/store';
import { connectUserStream } from 'themes/glitch/actions/streaming';
import { IntlProvider, addLocaleData } from 'react-intl';
-import { getLocale } from 'mastodon/locales';
+import { getLocale } from 'locales';
import initialState from 'themes/glitch/util/initial_state';
const { localeData, messages } = getLocale();
diff --git a/app/javascript/themes/glitch/packs/common.js b/app/javascript/themes/glitch/packs/common.js
index 3a62700bd1..f4fa129e1a 100644
--- a/app/javascript/themes/glitch/packs/common.js
+++ b/app/javascript/themes/glitch/packs/common.js
@@ -1,3 +1,3 @@
import 'font-awesome/css/font-awesome.css';
-require.context('../../images/', true);
-import './styles/index.scss';
+require.context('images/', true);
+import 'themes/glitch/styles/index.scss';
diff --git a/app/javascript/themes/glitch/packs/home.js b/app/javascript/themes/glitch/packs/home.js
index dada28317a..69dddf51c1 100644
--- a/app/javascript/themes/glitch/packs/home.js
+++ b/app/javascript/themes/glitch/packs/home.js
@@ -1,7 +1,7 @@
-import loadPolyfills from './util/load_polyfills';
+import loadPolyfills from 'themes/glitch/util/load_polyfills';
loadPolyfills().then(() => {
- require('./util/main').default();
+ require('themes/glitch/util/main').default();
}).catch(e => {
console.error(e);
});
diff --git a/app/javascript/themes/glitch/packs/public.js b/app/javascript/themes/glitch/packs/public.js
index 6adacad984..d9a1b96559 100644
--- a/app/javascript/themes/glitch/packs/public.js
+++ b/app/javascript/themes/glitch/packs/public.js
@@ -2,32 +2,14 @@ import loadPolyfills from 'themes/glitch/util/load_polyfills';
import { processBio } from 'themes/glitch/util/bio_metadata';
import ready from 'themes/glitch/util/ready';
-window.addEventListener('message', e => {
- const data = e.data || {};
-
- if (!window.parent || data.type !== 'setHeight') {
- return;
- }
-
- ready(() => {
- window.parent.postMessage({
- type: 'setHeight',
- id: data.id,
- height: document.getElementsByTagName('html')[0].scrollHeight,
- }, '*');
- });
-});
-
function main() {
- const { length } = require('stringz');
const IntlRelativeFormat = require('intl-relativeformat').default;
- const { delegate } = require('rails-ujs');
- const emojify = require('../themes/glitch/util/emoji').default;
- const { getLocale } = require('mastodon/locales');
+ const emojify = require('themes/glitch/util/emoji').default;
+ const { getLocale } = require('locales');
const { localeData } = getLocale();
- const VideoContainer = require('../themes/glitch/containers/video_container').default;
- const MediaGalleryContainer = require('../themes/glitch/containers/media_gallery_container').default;
- const CardContainer = require('../themes/glitch/containers/card_container').default;
+ const VideoContainer = require('themes/glitch/containers/video_container').default;
+ const MediaGalleryContainer = require('themes/glitch/containers/media_gallery_container').default;
+ const CardContainer = require('themes/glitch/containers/card_container').default;
const React = require('react');
const ReactDOM = require('react-dom');
@@ -87,61 +69,6 @@ function main() {
ReactDOM.render(, content);
});
});
-
- 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;
- });
-
- 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})`;
- });
}
loadPolyfills().then(main).catch(error => {
diff --git a/app/javascript/themes/glitch/theme.yml b/app/javascript/themes/glitch/theme.yml
index cf3fa32c2d..ac6f57546c 100644
--- a/app/javascript/themes/glitch/theme.yml
+++ b/app/javascript/themes/glitch/theme.yml
@@ -1,25 +1,34 @@
# (REQUIRED) The location of the pack files.
pack:
about: packs/about.js
- admin: null
- common: packs/common.js
- embed: null
- home: packs/home.js
+ admin:
+ auth:
+ common:
+ filename: packs/common.js
+ stylesheet: true
+ embed: packs/public.js
+ error:
+ home:
+ filename: packs/home.js
+ preload:
+ - themes/glitch/async/getting_started
+ - themes/glitch/async/compose
+ - themes/glitch/async/home_timeline
+ - themes/glitch/async/notifications
+ stylesheet: true
+ modal:
public: packs/public.js
- settings: null
+ settings:
share: packs/share.js
# (OPTIONAL) The directory which contains the pack files.
# Defaults to the theme directory (`app/javascript/themes/[theme]`),
# which should be sufficient for like 99% of use-cases lol.
-# pack_directory: app/javascript/packs
-# (OPTIONAL) Additional javascript resources to preload, for use with
-# lazy-loaded components. It is **STRONGLY RECOMMENDED** that you
-# derive these pathnames from `themes/[your-theme]` to ensure that
-# they stay unique.
-preload:
-- themes/glitch/async/getting_started
-- themes/glitch/async/compose
-- themes/glitch/async/home_timeline
-- themes/glitch/async/notifications
+# pack_directory: app/javascript/packs
+
+# (OPTIONAL) By default the theme will fallback to the default theme
+# if a particular pack is not provided. You can specify different
+# fallbacks here, or disable fallback behaviours altogether by
+# specifying a `null` value.
+fallback:
diff --git a/app/javascript/themes/vanilla/theme.yml b/app/javascript/themes/vanilla/theme.yml
index b4a1598fc2..67fd9723e8 100644
--- a/app/javascript/themes/vanilla/theme.yml
+++ b/app/javascript/themes/vanilla/theme.yml
@@ -1,12 +1,23 @@
# (REQUIRED) The location of the pack files inside `pack_directory`.
pack:
about: about.js
- admin: null
- common: common.js
- embed: null
- home: application.js
+ admin:
+ auth:
+ common:
+ filename: common.js
+ stylesheet: true
+ embed: public.js
+ error:
+ home:
+ filename: application.js
+ preload:
+ - features/getting_started
+ - features/compose
+ - features/home_timeline
+ - features/notifications
+ modal:
public: public.js
- settings: null
+ settings:
share: share.js
# (OPTIONAL) The directory which contains the pack files.
@@ -15,12 +26,8 @@ pack:
# somewhere else.
pack_directory: app/javascript/packs
-# (OPTIONAL) Additional javascript resources to preload, for use with
-# lazy-loaded components. It is **STRONGLY RECOMMENDED** that you
-# derive these pathnames from `themes/[your-theme]` to ensure that
-# they stay unique. (Of course, vanilla doesn't do this ^^;;)
-preload:
-- features/getting_started
-- features/compose
-- features/home_timeline
-- features/notifications
+# (OPTIONAL) By default the theme will fallback to the default theme
+# if a particular pack is not provided. You can specify different
+# fallbacks here, or disable fallback behaviours altogether by
+# specifying a `null` value.
+fallback:
diff --git a/app/javascript/themes/win95/index.js b/app/javascript/themes/win95/index.js
new file mode 100644
index 0000000000..bed6a1ef3d
--- /dev/null
+++ b/app/javascript/themes/win95/index.js
@@ -0,0 +1,10 @@
+// These lines are the same as in glitch:
+import 'font-awesome/css/font-awesome.css';
+require.context('../../images/', true);
+
+// …But we want to use our own styles instead.
+import 'styles/win95.scss';
+
+// Be sure to make this style file import from
+// `themes/glitch/styles/index.scss` (the glitch styling), and not
+// `application.scss` (which are the vanilla styles).
diff --git a/app/javascript/themes/win95/theme.yml b/app/javascript/themes/win95/theme.yml
new file mode 100644
index 0000000000..43af381989
--- /dev/null
+++ b/app/javascript/themes/win95/theme.yml
@@ -0,0 +1,23 @@
+# win95 theme.
+
+# Ported over from `cybrespace:mastodon/theme_win95`.
+#
+
+# You can use this theme file as inspiration for porting over
+# a preëxisting Mastodon theme.
+
+# We only modify the `common` pack, which contains our styling.
+pack:
+ common:
+ filename: index.js
+ stylesheet: true
+ # All unspecified packs will inherit from glitch.
+
+# By default, the glitch preloads will also be used here. You can
+# disable them by setting `preload` to `null`.
+
+# preload:
+
+# The `fallback` parameter tells us to use glitch files for everything
+# we haven't specified.
+fallback: glitch
diff --git a/app/lib/themes.rb b/app/lib/themes.rb
index f7ec22fd2b..7ced9f945a 100644
--- a/app/lib/themes.rb
+++ b/app/lib/themes.rb
@@ -7,15 +7,27 @@ class Themes
include Singleton
def initialize
+
+ core = YAML.load_file(Rails.root.join('app', 'javascript', 'core', 'theme.yml'))
+ core['pack'] = Hash.new unless core['pack']
+
result = Hash.new
Dir.glob(Rails.root.join('app', 'javascript', 'themes', '*', 'theme.yml')) do |path|
data = YAML.load_file(path)
name = File.basename(File.dirname(path))
if data['pack']
+ data['name'] = name
result[name] = data
end
end
+
+ @core = core
@conf = result
+
+ end
+
+ def core
+ @core
end
def get(name)
diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml
index 7ffa5ecc35..d92362bd76 100644
--- a/app/views/about/more.html.haml
+++ b/app/views/about/more.html.haml
@@ -2,7 +2,6 @@
= site_hostname
- content_for :header_tags do
- = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= render partial: 'shared/og'
.landing-page
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index 385b0b1dc3..4f5b53470a 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -3,7 +3,6 @@
- content_for :header_tags do
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
- = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
= render partial: 'shared/og'
.landing-page
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 5747cc2740..7dd962bf23 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -1,6 +1,3 @@
-- content_for :header_tags do
- = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
-
- content_for :page_title do
= t('admin.reports.report', id: @report.id)
diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml
index fe25815274..9747a92cf0 100644
--- a/app/views/admin/statuses/index.html.haml
+++ b/app/views/admin/statuses/index.html.haml
@@ -1,6 +1,3 @@
-- content_for :header_tags do
- = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
-
- content_for :page_title do
= t('admin.statuses.title')
diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml
index 63b3a0c26b..e8a81656cc 100644
--- a/app/views/home/index.html.haml
+++ b/app/views/home/index.html.haml
@@ -1,13 +1,7 @@
- content_for :header_tags do
- - if theme_data['preload']
- - theme_data['preload'].each do |link|
- %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
%meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key}
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
- = javascript_pack_tag "themes/#{current_theme}", integrity: true, crossorigin: 'anonymous'
- = stylesheet_pack_tag "themes/#{current_theme}", integrity: true, media: 'all'
-
.app-holder#mastodon{ data: { props: Oj.dump(default_props) } }
%noscript
= image_tag asset_pack_path('logo.svg'), alt: 'Mastodon'
diff --git a/app/views/layouts/_theme.html.haml b/app/views/layouts/_theme.html.haml
new file mode 100644
index 0000000000..cdec4b370d
--- /dev/null
+++ b/app/views/layouts/_theme.html.haml
@@ -0,0 +1,10 @@
+- if theme
+ - if theme[:pack] != 'common' && theme[:common]
+ = render partial: 'layouts/theme', object: theme[:common]
+ - if theme[:pack]
+ = javascript_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, crossorigin: 'anonymous'
+ - if theme[:stylesheet]
+ = stylesheet_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, media: 'all'
+ - if theme[:preload]
+ - theme[:preload].each do |link|
+ %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index c98d85f7bf..66382db500 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,6 +1,3 @@
-- content_for :header_tags do
- = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
-
- content_for :content do
.admin-wrapper
.sidebar-wrapper
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 24b74c787c..99ae7d90d6 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -18,16 +18,16 @@
= ' - '
= title
- = stylesheet_pack_tag 'common', media: 'all'
- = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
+ = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
= csrf_meta_tags
- - if controller_name != 'home'
- = stylesheet_pack_tag 'application', integrity: true, media: 'all'
-
= yield :header_tags
+ -# These must come after :header_tags to ensure our initial state has been defined.
+ = render partial: 'layouts/theme', object: @core
+ = render partial: 'layouts/theme', object: @theme
+
- body_classes ||= @body_classes || ''
- body_classes += ' system-font' if current_account&.user&.setting_system_font_ui
diff --git a/app/views/layouts/auth.html.haml b/app/views/layouts/auth.html.haml
index d8ac733f91..f4812ac6a3 100644
--- a/app/views/layouts/auth.html.haml
+++ b/app/views/layouts/auth.html.haml
@@ -1,6 +1,3 @@
-- content_for :header_tags do
- = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
-
- content_for :content do
.container
.logo-container
diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml
index 5fc60be170..3960167bf8 100644
--- a/app/views/layouts/embedded.html.haml
+++ b/app/views/layouts/embedded.html.haml
@@ -4,10 +4,9 @@
%meta{ charset: 'utf-8' }/
%meta{ name: 'robots', content: 'noindex' }/
- = stylesheet_pack_tag 'common', media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
- = stylesheet_pack_tag 'application', integrity: true, media: 'all'
+ = javascript_pack_tag 'embed', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
- = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
- %body.embed
+ = render partial: 'layouts/theme', object: @core
+ = render partial: 'layouts/theme', object: @theme
= yield
diff --git a/app/views/layouts/error.html.haml b/app/views/layouts/error.html.haml
index d0eae44346..9904b8fdd7 100644
--- a/app/views/layouts/error.html.haml
+++ b/app/views/layouts/error.html.haml
@@ -5,8 +5,8 @@
%meta{ charset: 'utf-8' }/
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
- = stylesheet_pack_tag 'common', media: 'all'
- = stylesheet_pack_tag 'application', integrity: true, media: 'all'
+ = render partial: 'layouts/theme', object: @core
+ = render partial: 'layouts/theme', object: @theme
%body.error
.dialog
%img{ alt: Setting.default_settings['site_title'], src: '/oops.gif' }/
diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml
index a819e098d6..d3519f032f 100644
--- a/app/views/layouts/modal.html.haml
+++ b/app/views/layouts/modal.html.haml
@@ -1,6 +1,3 @@
-- content_for :header_tags do
- = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
-
- content_for :content do
- if user_signed_in?
.account-header
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index 83e92b938f..b3795eaadd 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -1,6 +1,3 @@
-- content_for :header_tags do
- = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
-
- content_for :content do
.container= yield
.footer
diff --git a/app/views/shares/show.html.haml b/app/views/shares/show.html.haml
index 44b6f145f6..4c0390c421 100644
--- a/app/views/shares/show.html.haml
+++ b/app/views/shares/show.html.haml
@@ -1,5 +1,4 @@
- content_for :header_tags do
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
- = javascript_pack_tag 'share', integrity: true, crossorigin: 'anonymous'
#mastodon-compose{ data: { props: Oj.dump(default_props) } }
diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml
index ea8b0faa33..e05fe1c395 100644
--- a/app/views/tags/show.html.haml
+++ b/app/views/tags/show.html.haml
@@ -3,7 +3,6 @@
- content_for :header_tags do
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
- = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
= render 'og'
.landing-page.tag-page
diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js
index 74f75d89b3..9514bc547a 100644
--- a/config/webpack/configuration.js
+++ b/config/webpack/configuration.js
@@ -12,14 +12,24 @@ const settings = safeLoad(readFileSync(configPath), 'utf8')[env.NODE_ENV];
const themeFiles = glob.sync('app/javascript/themes/*/theme.yml');
const themes = {};
+const core = function () {
+ const coreFile = resolve('app', 'javascript', 'core', 'theme.yml');
+ const data = safeLoad(readFileSync(coreFile), 'utf8');
+ if (!data.pack_directory) {
+ data.pack_directory = dirname(coreFile);
+ }
+ return data.pack ? data : {};
+}();
+
for (let i = 0; i < themeFiles.length; i++) {
const themeFile = themeFiles[i];
const data = safeLoad(readFileSync(themeFile), 'utf8');
+ data.name = basename(dirname(themeFile));
if (!data.pack_directory) {
data.pack_directory = dirname(themeFile);
}
if (data.pack) {
- themes[basename(dirname(themeFile))] = data;
+ themes[data.name] = data;
}
}
@@ -43,6 +53,7 @@ const output = {
module.exports = {
settings,
+ core,
themes,
env,
loadersDir,
diff --git a/config/webpack/generateLocalePacks.js b/config/webpack/generateLocalePacks.js
index cd3bed50c0..a943589f74 100644
--- a/config/webpack/generateLocalePacks.js
+++ b/config/webpack/generateLocalePacks.js
@@ -57,7 +57,7 @@ Object.keys(glitchMessages).forEach(function (key) {
//
import messages from '../../app/javascript/mastodon/locales/${locale}.json';
import localeData from ${JSON.stringify(localeDataPath)};
-import { setLocale } from '../../app/javascript/mastodon/locales';
+import { setLocale } from 'locales';
${glitchInject}
setLocale({messages: mergedMessages, localeData: localeData});
`;
diff --git a/config/webpack/shared.js b/config/webpack/shared.js
index 5d176db4eb..5b90f27fb5 100644
--- a/config/webpack/shared.js
+++ b/config/webpack/shared.js
@@ -6,33 +6,37 @@ const { sync } = require('glob');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const extname = require('path-complete-extname');
-const { env, settings, themes, output, loadersDir } = require('./configuration.js');
+const { env, settings, core, themes, output, loadersDir } = require('./configuration.js');
const localePackPaths = require('./generateLocalePacks');
-const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
-const entryPath = join(settings.source_path, settings.source_entry_path);
-const packPaths = sync(join(entryPath, extensionGlob));
+function reducePacks (data, into = {}) {
+ if (!data.pack) {
+ return into;
+ }
+ Object.keys(data.pack).reduce((map, entry) => {
+ const pack = data.pack[entry];
+ if (!pack) {
+ return map;
+ }
+ const packFile = typeof pack === 'string' ? pack : pack.filename;
+ if (packFile) {
+ map[data.name ? `themes/${data.name}/${entry}` : `core/${entry}`] = resolve(data.pack_directory, packFile);
+ }
+ return map;
+ }, into);
+ return into;
+}
module.exports = {
entry: Object.assign(
- packPaths.reduce((map, entry) => {
- const localMap = map;
- const namespace = relative(join(entryPath), dirname(entry));
- localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
- return localMap;
- }, {}),
+ { locales: resolve('app', 'javascript', 'locales') },
localePackPaths.reduce((map, entry) => {
const localMap = map;
localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry);
return localMap;
}, {}),
- Object.keys(themes).reduce(
- (themePaths, name) => {
- const themeData = themes[name];
- themePaths[`themes/${name}`] = resolve(themeData.pack_directory, themeData.pack);
- return themePaths;
- }, {}
- )
+ reducePacks(core),
+ Object.keys(themes).reduce((map, entry) => reducePacks(themes[entry], map), {})
),
output: {
@@ -64,7 +68,7 @@ module.exports = {
writeToFileEmit: true,
}),
new webpack.optimize.CommonsChunkPlugin({
- name: 'common',
+ name: 'locales',
minChunks: Infinity, // It doesn't make sense to use common chunks with multiple frontend support.
}),
],
diff --git a/config/webpacker.yml b/config/webpacker.yml
index 8d8470651a..50d95813a5 100644
--- a/config/webpacker.yml
+++ b/config/webpacker.yml
@@ -2,7 +2,6 @@
default: &default
source_path: app/javascript
- source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
@@ -13,17 +12,6 @@ default: &default
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
- extensions:
- - .js
- - .sass
- - .scss
- - .css
- - .png
- - .svg
- - .gif
- - .jpeg
- - .jpg
-
development:
<<: *default
compile: true