end
def embed
- return not_found if @status.hidden?
+ use_pack 'embed'
+ return not_found if @status.hidden? || @status.reblog?
expires_in 180, public: true
response.headers['X-Frame-Options'] = 'ALLOWALL'
--- /dev/null
- name.textContent = document.querySelector('#default_account_display_name').textContent;
+// This file will be loaded on settings pages, regardless of theme.
+
+import escapeTextContentForBrowser from 'escape-html';
+const { delegate } = require('@rails/ujs');
+import emojify from '../mastodon/features/emoji/emoji';
+
+delegate(document, '#account_display_name', 'input', ({ target }) => {
+ const name = document.querySelector('.card .display-name strong');
+ if (name) {
+ if (target.value) {
+ name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
+ } else {
++ name.textContent = name.textContent = target.dataset.default;
+ }
+ }
+});
+
+delegate(document, '#account_avatar', 'change', ({ target }) => {
+ const avatar = document.querySelector('.card .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 .card__img img');
+ const [file] = target.files || [];
+ const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
+
+ header.src = url;
+});
+
+delegate(document, '#account_locked', 'change', ({ target }) => {
+ const lock = document.querySelector('.card .display-name i');
+
+ if (target.checked) {
+ lock.style.display = 'inline';
+ } else {
+ lock.style.display = 'none';
+ }
+});
+
+delegate(document, '.input-copy input', 'click', ({ target }) => {
+ target.focus();
+ target.select();
+ target.setSelectionRange(0, target.value.length);
+});
+
+delegate(document, '.input-copy button', 'click', ({ target }) => {
+ const input = target.parentNode.querySelector('.input-copy__wrapper input');
+
+ const oldReadOnly = input.readonly;
+
+ input.readonly = false;
+ input.focus();
+ input.select();
+ input.setSelectionRange(0, input.value.length);
+
+ try {
+ if (document.execCommand('copy')) {
+ input.blur();
+ target.parentNode.classList.add('copied');
+
+ setTimeout(() => {
+ target.parentNode.classList.remove('copied');
+ }, 700);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+
+ input.readonly = oldReadOnly;
+});
--- /dev/null
- const contentEl = this.parentNode.parentNode.querySelector('.e-content');
+import loadPolyfills from 'flavours/glitch/util/load_polyfills';
+import ready from 'flavours/glitch/util/ready';
+import loadKeyboardExtensions from 'flavours/glitch/util/load_keyboard_extensions';
+
+function main() {
+ const IntlMessageFormat = require('intl-messageformat').default;
+ const { timeAgoString } = require('flavours/glitch/components/relative_timestamp');
+ const { delegate } = require('@rails/ujs');
+ const emojify = require('flavours/glitch/util/emoji').default;
+ const { getLocale } = require('locales');
+ const { messages } = getLocale();
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+ const Rellax = require('rellax');
+ const { createBrowserHistory } = require('history');
+
+ const scrollToDetailedStatus = () => {
+ const history = createBrowserHistory();
+ const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status');
+ const location = history.location;
+
+ if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) {
+ detailedStatuses[0].scrollIntoView();
+ history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true });
+ }
+ };
+
+ const getEmojiAnimationHandler = (swapTo) => {
+ return ({ target }) => {
+ target.src = target.getAttribute(swapTo);
+ };
+ };
+
+ ready(() => {
+ const locale = document.documentElement.lang;
+
+ const dateTimeFormat = new Intl.DateTimeFormat(locale, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ });
+
+ [].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'));
+ const now = new Date();
+
+ content.title = dateTimeFormat.format(datetime);
+ content.textContent = timeAgoString({
+ formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
+ formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
+ }, datetime, now, now.getFullYear(), content.getAttribute('datetime').includes('T'));
+ });
+
+ const reactComponents = document.querySelectorAll('[data-component]');
+ if (reactComponents.length > 0) {
+ import(/* webpackChunkName: "containers/media_container" */ 'flavours/glitch/containers/media_container')
+ .then(({ default: MediaContainer }) => {
+ [].forEach.call(reactComponents, (component) => {
+ [].forEach.call(component.children, (child) => {
+ component.removeChild(child);
+ });
+ });
+
+ const content = document.createElement('div');
+
+ ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
+ document.body.appendChild(content);
+ scrollToDetailedStatus();
+ })
+ .catch(error => {
+ console.error(error);
+ scrollToDetailedStatus();
+ });
+ } else {
+ scrollToDetailedStatus();
+ }
+
+ const parallaxComponents = document.querySelectorAll('.parallax');
+
+ if (parallaxComponents.length > 0 ) {
+ new Rellax('.parallax', { speed: -1 });
+ }
+
+ delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
+ delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
+
+ delegate(document, '.status__content__spoiler-link', 'click', function() {
- if (contentEl.style.display === 'block') {
- contentEl.style.display = 'none';
- this.parentNode.style.marginBottom = 0;
++ const statusEl = this.parentNode.parentNode;
+
- contentEl.style.display = 'block';
- this.parentNode.style.marginBottom = null;
++ if (statusEl.dataset.spoiler === 'expanded') {
++ statusEl.dataset.spoiler = 'folded';
+ this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format();
+ } else {
- const contentEl = spoilerLink.parentNode.parentNode.querySelector('.e-content');
- const message = (contentEl.style.display === 'block') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
++ statusEl.dataset.spoiler = 'expanded';
+ this.textContent = (new IntlMessageFormat(messages['status.show_less'] || 'Show less', locale)).format();
+ }
+
+ return false;
+ });
+
+ [].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
++ const statusEl = spoilerLink.parentNode.parentNode;
++ const message = (statusEl.dataset.spoiler === 'expanded') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
+ spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
+ });
+ });
+
+ delegate(document, '.sidebar__toggle__icon', 'click', () => {
+ const target = document.querySelector('.sidebar ul');
+
+ if (target.style.display === 'block') {
+ target.style.display = 'none';
+ } else {
+ target.style.display = 'block';
+ }
+ });
+}
+
+loadPolyfills()
+ .then(main)
+ .then(loadKeyboardExtensions)
+ .catch(error => {
+ console.error(error);
+ });
visibility = [:public, :unlisted]
if account.nil?
- where(visibility: visibility)
+ where(visibility: visibility).not_local_only
- elsif target_account.blocking?(account) # get rid of blocked peeps
+ elsif target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain)) # get rid of blocked peeps
none
elsif account.id == target_account.id # author can see own stuff
all
include RoutingHelper
attributes :uri, :title, :short_description, :description, :email,
- :version, :urls, :stats, :thumbnail,
+ :version, :urls, :stats, :thumbnail, :max_toot_chars, :poll_limits,
- :languages, :registrations, :approval_required
+ :languages, :registrations, :approval_required, :invites_enabled
has_one :contact_account, serializer: REST::AccountSerializer
= button_tag t('generic.save_changes'), class: 'button', form: 'edit_user'
= simple_form_for current_user, url: settings_preferences_appearance_path, html: { method: :put, id: 'edit_user' } do |f|
- .fields-row
- .fields-group.fields-row__column.fields-row__column-6
- = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale, hint: false
- .fields-group.fields-row__column.fields-row__column-6
- = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: false
+ .fields-group
+ = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale, hint: false
- unless I18n.locale == :en
- .flash-message{ style: "text-align: unset; color: unset" }
- #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener", style: "text-decoration: underline")}
+ .flash-message.translation-prompt
+ #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener")}
%h4= t 'appearance.advanced_web_interface'
.fields-row
.fields-row__column.fields-group.fields-row__column-6
- = f.input :display_name, wrapper: :with_label, input_html: { maxlength: Account::MAX_DISPLAY_NAME_LENGTH }, hint: false
- = f.input :display_name, wrapper: :with_label, input_html: { maxlength: 30, data: { default: @account.username } }, hint: false
- = f.input :note, wrapper: :with_label, input_html: { maxlength: 500 }, hint: false
++ = f.input :display_name, wrapper: :with_label, input_html: { maxlength: Account::MAX_DISPLAY_NAME_LENGTH, data: { default: @account.username } }, hint: false
+ = f.input :note, wrapper: :with_label, input_html: { maxlength: Account::MAX_NOTE_LENGTH }, hint: false
.fields-row
.fields-row__column.fields-row__column-6
%span.display-name__account
= acct(status.account)
= fa_icon('lock') if status.account.locked?
- .status__content.emojify<
+ .status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
- if status.spoiler_text?
- %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
+ %p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}
%button.status__content__spoiler-link= t('statuses.show_more')
- .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
- .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
++ .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }<
= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
- if status.preloadable_poll
= react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do