diff options
73 files changed, 566 insertions, 371 deletions
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 47690e81e..8785df14e 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 75915b337..309cb65da 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 db4839a8f..726134509 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 + layout 'admin' + before_action :require_staff! + before_action :set_pack - layout 'admin' + def set_pack + use_pack 'admin' + end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f5dbe837e..7cc4eea27 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 d5e8e58ed..5ffa1c9a3 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 171b997dc..e0400aa3d 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 223db96ff..cc7a69ab0 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 a5acb6c36..72d544102 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 78b564183..2d29bd379 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 399e79665..080cbde11 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 1e73d4bd4..74e83ad81 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 21dde20ce..7437a647e 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 48b026aa5..e6f379886 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 8fc9a0fa9..35a6f7f9e 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 000000000..7322d461b --- /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 80002b995..e4cb35a8e 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 ae62f00c1..9c03ece86 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 9968504e5..141b2270d 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 0db13d1ca..dbd136ebe 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 f79e1b320..699b8a3ef 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 ce2530c54..6286e3ebf 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 069026715..3aefd90a2 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 28f78a4fb..dadc3d911 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 0da1b027b..780ea64b4 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 863cc7351..8c7737e9d 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 994742c3d..81d279c8b 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 e8a360fb5..84c9e7685 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 5f61e2182..b597ba4bb 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 9f3090e37..5d11a8139 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 6ed0e4ad3..000000000 --- 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 8167706a3..6146e6592 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 3c2e01590..000000000 --- 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 1a36b7a5f..47c34a259 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 91332ed5a..7fb1d8e77 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 (nameCounter) { + nameCounter.textContent = 30 - length(target.value); + } +}); - if (noteCounter) { - const noteWithoutMetadata = processBio(target.value).text; - noteCounter.textContent = 500 - length(noteWithoutMetadata); - } - }); +delegate(document, '.account_note', 'input', ({ target }) => { + const noteCounter = document.querySelector('.note-counter'); - 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; + if (noteCounter) { + const noteWithoutMetadata = processBio(target.value).text; + noteCounter.textContent = 500 - length(noteWithoutMetadata); + } +}); - avatar.src = url; - }); +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; - 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; + avatar.src = url; +}); - header.style.backgroundImage = `url(${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; -loadPolyfills().then(main).catch(error => { - console.error(error); + header.style.backgroundImage = `url(${url})`; }); diff --git a/app/javascript/core/share.js b/app/javascript/core/share.js deleted file mode 100644 index 98a413632..000000000 --- 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 000000000..17e8e66b3 --- /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 000000000..421cb7fab --- /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 421cb7fab..7e7297561 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 000000000..63e12da42 --- /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(<TimelineContainer {...props} />, 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 ee5bf244c..116632dea 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 000000000..f3156c1c6 --- /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 000000000..3472af6c1 --- /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(<VideoContainer locale={locale} {...props} />, content); + }); + + [].forEach.call(document.querySelectorAll('[data-component="MediaGallery"]'), (content) => { + const props = JSON.parse(content.getAttribute('data-props')); + ReactDOM.render(<MediaGalleryContainer locale={locale} {...props} />, content); + }); + + [].forEach.call(document.querySelectorAll('[data-component="Card"]'), (content) => { + const props = JSON.parse(content.getAttribute('data-props')); + ReactDOM.render(<CardContainer locale={locale} {...props} />, 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 000000000..e9580f648 --- /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(<ComposeContainer {...props} />, 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 c1772e7ae..000000000 --- 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 885837b53..6c89fc5bf 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 348470637..755b5564a 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 3a62700bd..f4fa129e1 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 dada28317..69dddf51c 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 6adacad98..d9a1b9655 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(<CardContainer locale={locale} {...props} />, 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 cf3fa32c2..ac6f57546 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 b4a1598fc..67fd9723e 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 000000000..bed6a1ef3 --- /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 000000000..43af38198 --- /dev/null +++ b/app/javascript/themes/win95/theme.yml @@ -0,0 +1,23 @@ +# win95 theme. + +# Ported over from `cybrespace:mastodon/theme_win95`. +# <https://github.com/cybrespace/mastodon/tree/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 f7ec22fd2..7ced9f945 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 7ffa5ecc3..d92362bd7 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 385b0b1dc..4f5b53470 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 5747cc274..7dd962bf2 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 fe2581527..9747a92cf 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 63b3a0c26..e8a81656c 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 000000000..cdec4b370 --- /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 c98d85f7b..66382db50 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 24b74c787..99ae7d90d 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 d8ac733f9..f4812ac6a 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 5fc60be17..3960167bf 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 d0eae4434..9904b8fdd 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 a819e098d..d3519f032 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 83e92b938..b3795eaad 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 44b6f145f..4c0390c42 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 ea8b0faa3..e05fe1c39 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 74f75d89b..9514bc547 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 cd3bed50c..a943589f7 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 5d176db4e..5b90f27fb 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 8d8470651..50d95813a 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 |