diff options
Diffstat (limited to 'app')
47 files changed, 1361 insertions, 1214 deletions
diff --git a/app/controllers/concerns/account_controller_concern.rb b/app/controllers/concerns/account_controller_concern.rb index 5b9981aa2..6c27ef330 100644 --- a/app/controllers/concerns/account_controller_concern.rb +++ b/app/controllers/concerns/account_controller_concern.rb @@ -8,6 +8,7 @@ module AccountControllerConcern included do layout 'public' before_action :set_account + before_action :set_instance_presenter before_action :set_link_headers before_action :check_account_suspension end @@ -18,6 +19,10 @@ module AccountControllerConcern @account = Account.find_local!(params[:account_username]) end + def set_instance_presenter + @instance_presenter = InstancePresenter.new + end + def set_link_headers response.headers['Link'] = LinkHeader.new( [ diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index b85341822..755af1deb 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -12,6 +12,7 @@ class StatusesController < ApplicationController before_action :set_account before_action :set_status + before_action :set_instance_presenter before_action :set_link_headers before_action :check_account_suspension before_action :redirect_to_original, only: [:show] @@ -148,6 +149,10 @@ class StatusesController < ApplicationController raise ActiveRecord::RecordNotFound end + def set_instance_presenter + @instance_presenter = InstancePresenter.new + end + def check_account_suspension gone if @account.suspended? end diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 05cea73d7..121644263 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -12,6 +12,52 @@ module StreamEntriesHelper end end + def account_action_button(account) + if user_signed_in? + if account.id == current_user.account_id + link_to settings_profile_url, class: 'button logo-button' do + safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('settings.edit_profile')]) + end + elsif current_account.following?(account) || current_account.requested?(account) + link_to account_unfollow_path(account), class: 'button logo-button', data: { method: :post } do + safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')]) + end + else + link_to account_follow_path(account), class: 'button logo-button', data: { method: :post } do + safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')]) + end + end + else + link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do + safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')]) + end + end + end + + def account_badge(account) + if account.bot? + content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles') + elsif Setting.show_staff_badge && account.user_staff? + content_tag(:div, class: 'roles') do + if account.user_admin? + content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin') + elsif account.user_moderator? + content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator') + end + end + end + end + + def link_to_more(url) + link_to t('statuses.show_more'), url, class: 'load-more load-gap' + end + + def nothing_here(extra_classes = '') + content_tag(:div, class: "nothing-here #{extra_classes}") do + t('accounts.nothing_here') + end + end + def account_description(account) prepend_str = [ [ diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js index 3c8db7092..9609714a1 100644 --- a/app/javascript/mastodon/components/relative_timestamp.js +++ b/app/javascript/mastodon/components/relative_timestamp.js @@ -60,6 +60,32 @@ const getUnitDelay = units => { } }; +export const timeAgoString = (intl, date, now, year) => { + const delta = now - date.getTime(); + + let relativeTime; + + if (delta < 10 * SECOND) { + relativeTime = intl.formatMessage(messages.just_now); + } else if (delta < 7 * DAY) { + if (delta < MINUTE) { + relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) }); + } else if (delta < HOUR) { + relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) }); + } else if (delta < DAY) { + relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) }); + } else { + relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) }); + } + } else if (date.getFullYear() === year) { + relativeTime = intl.formatDate(date, shortDateFormatOptions); + } else { + relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' }); + } + + return relativeTime; +}; + @injectIntl export default class RelativeTimestamp extends React.Component { @@ -121,28 +147,8 @@ export default class RelativeTimestamp extends React.Component { render () { const { timestamp, intl, year } = this.props; - const date = new Date(timestamp); - const delta = this.state.now - date.getTime(); - - let relativeTime; - - if (delta < 10 * SECOND) { - relativeTime = intl.formatMessage(messages.just_now); - } else if (delta < 7 * DAY) { - if (delta < MINUTE) { - relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) }); - } else if (delta < HOUR) { - relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) }); - } else if (delta < DAY) { - relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) }); - } else { - relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) }); - } - } else if (date.getFullYear() === year) { - relativeTime = intl.formatDate(date, shortDateFormatOptions); - } else { - relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' }); - } + const date = new Date(timestamp); + const relativeTime = timeAgoString(intl, date, this.state.now, year); return ( <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}> diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index 4b1e87ae3..855e56ee6 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -22,15 +22,15 @@ window.addEventListener('message', e => { function main() { const { length } = require('stringz'); - const IntlRelativeFormat = require('intl-relativeformat').default; + const IntlMessageFormat = require('intl-messageformat').default; + const { timeAgoString } = require('../mastodon/components/relative_timestamp'); const { delegate } = require('rails-ujs'); const emojify = require('../mastodon/features/emoji/emoji').default; const { getLocale } = require('../mastodon/locales'); - const { localeData } = getLocale(); + const { messages } = getLocale(); const React = require('react'); const ReactDOM = require('react-dom'); - - localeData.forEach(IntlRelativeFormat.__addLocaleData); + const Rellax = require('rellax'); ready(() => { const locale = document.documentElement.lang; @@ -43,8 +43,6 @@ function main() { minute: 'numeric', }); - const relativeFormat = new IntlRelativeFormat(locale); - [].forEach.call(document.querySelectorAll('.emojify'), (content) => { content.innerHTML = emojify(content.innerHTML); }); @@ -59,12 +57,16 @@ function main() { [].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 = relativeFormat.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, datetime.getFullYear()); }); - [].forEach.call(document.querySelectorAll('.logo-button'), (content) => { + [].forEach.call(document.querySelectorAll('.modal-button'), (content) => { content.addEventListener('click', (e) => { e.preventDefault(); window.open(e.target.href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); @@ -82,6 +84,8 @@ function main() { }) .catch(error => console.error(error)); } + + new Rellax('.parallax', { speed: -1 }); }); delegate(document, '.webapp-btn', 'click', ({ target, button }) => { @@ -106,15 +110,20 @@ function main() { return false; }); - delegate(document, '.account_display_name', 'input', ({ target }) => { + delegate(document, '#account_display_name', 'input', ({ target }) => { const nameCounter = document.querySelector('.name-counter'); + const name = document.querySelector('.card .display-name strong'); if (nameCounter) { nameCounter.textContent = 30 - length(target.value); } + + if (name) { + name.innerHTML = emojify(target.value); + } }); - delegate(document, '.account_note', 'input', ({ target }) => { + delegate(document, '#account_note', 'input', ({ target }) => { const noteCounter = document.querySelector('.note-counter'); if (noteCounter) { @@ -123,7 +132,7 @@ function main() { }); delegate(document, '#account_avatar', 'change', ({ target }) => { - const avatar = document.querySelector('.card.compact .avatar img'); + const avatar = document.querySelector('.card .avatar img'); const [file] = target.files || []; const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; @@ -131,11 +140,21 @@ function main() { }); delegate(document, '#account_header', 'change', ({ target }) => { - const header = document.querySelector('.card.compact'); + const header = document.querySelector('.card .card__img img'); const [file] = target.files || []; const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc; - header.style.backgroundImage = `url(${url})`; + 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'; + } }); } diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index 7b3b10dfe..0990a4f25 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -10,7 +10,7 @@ @import 'mastodon/lists'; @import 'mastodon/footer'; @import 'mastodon/compact_header'; -@import 'mastodon/landing_strip'; +@import 'mastodon/widgets'; @import 'mastodon/forms'; @import 'mastodon/accounts'; @import 'mastodon/stream_entries'; diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index b4612b063..c27bc0df3 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -1,243 +1,100 @@ .card { - background-color: $base-shadow-color; - background-size: cover; - background-position: center; - border-radius: 4px 4px 0 0; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - overflow: hidden; - position: relative; - display: flex; - - &::after { - background: rgba(darken($ui-base-color, 8%), 0.5); + & > a { display: block; - content: ""; - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - z-index: 1; - } - - @media screen and (max-width: 740px) { - border-radius: 0; - box-shadow: none; - } - - .card__illustration { - padding: 60px 0; - position: relative; - flex: 1 1 auto; - display: flex; - justify-content: center; - align-items: center; - } - - .card__bio { - max-width: 260px; - flex: 1 1 auto; - display: flex; - flex-direction: column; - justify-content: space-between; - background: rgba(darken($ui-base-color, 8%), 0.8); - position: relative; - z-index: 2; - } - - &.compact { - padding: 30px 0; - border-radius: 4px; - - .avatar { - margin-bottom: 0; + text-decoration: none; + color: inherit; + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - img { - object-fit: cover; - } + @media screen and (max-width: $no-gap-breakpoint) { + box-shadow: none; } - } - - .name { - display: block; - font-size: 20px; - line-height: 18px * 1.5; - color: $primary-text-color; - padding: 10px 15px; - padding-bottom: 0; - font-weight: 500; - position: relative; - z-index: 2; - margin-bottom: 30px; - overflow: hidden; - text-overflow: ellipsis; - small { - display: block; - font-size: 14px; - color: $highlight-text-color; - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - - .fa { - margin-left: 3px; + &:hover, + &:active, + &:focus { + .card__bar { + background: lighten($ui-base-color, 8%); } } } - .avatar { - width: 120px; - margin: 0 auto; + &__img { + height: 130px; position: relative; - z-index: 2; + background: darken($ui-base-color, 12%); + border-radius: 4px 4px 0 0; img { - width: 120px; - height: 120px; - display: block; - border-radius: 120px; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - } - } - - .roles { - margin-bottom: 30px; - padding: 0 15px; - } - - .details-counters { - margin-top: 30px; - display: flex; - flex-direction: row; - width: 100%; - } - - .counter { - width: 33.3%; - box-sizing: border-box; - flex: 0 0 auto; - color: $darker-text-color; - padding: 5px 10px 0; - margin-bottom: 10px; - border-right: 1px solid lighten($ui-base-color, 4%); - cursor: default; - text-align: center; - position: relative; - - a { - display: block; - } - - &:last-child { - border-right: 0; - } - - &::after { display: block; - content: ""; - position: absolute; - bottom: -10px; - left: 0; width: 100%; - border-bottom: 4px solid $ui-primary-color; - opacity: 0.5; - transition: all 400ms ease; - } - - &.active { - &::after { - border-bottom: 4px solid $highlight-text-color; - opacity: 1; - } - } - - &:hover { - &::after { - opacity: 1; - transition-duration: 100ms; - } + height: 100%; + margin: 0; + object-fit: cover; + border-radius: 4px 4px 0 0; } - a { - text-decoration: none; - color: inherit; + @media screen and (max-width: 600px) { + height: 200px; } - .counter-label { - font-size: 12px; - display: block; - margin-bottom: 5px; - } - - .counter-number { - font-weight: 500; - font-size: 18px; - color: $primary-text-color; - font-family: 'mastodon-font-display', sans-serif; + @media screen and (max-width: $no-gap-breakpoint) { + display: none; } } - .bio { - font-size: 14px; - line-height: 18px; - padding: 0 15px; - color: $secondary-text-color; - } - - @media screen and (max-width: 480px) { - display: block; + &__bar { + position: relative; + padding: 15px; + display: flex; + justify-content: flex-start; + align-items: center; + background: lighten($ui-base-color, 4%); + border-radius: 0 0 4px 4px; - .card__bio { - max-width: none; + @media screen and (max-width: $no-gap-breakpoint) { + border-radius: 0; } - .name, - .roles { - text-align: center; - margin-bottom: 15px; - } + .avatar { + flex: 0 0 auto; + width: 48px; + height: 48px; + padding-top: 2px; - .bio { - margin-bottom: 15px; + img { + width: 100%; + height: 100%; + display: block; + margin: 0; + border-radius: 4px; + background: darken($ui-base-color, 8%); + } } - } -} -.card, -.account-grid-card { - .controls { - position: absolute; - top: 15px; - left: 15px; - z-index: 2; - - .icon-button { - color: rgba($white, 0.8); - text-decoration: none; - font-size: 13px; - line-height: 13px; - font-weight: 500; - - .fa { - font-weight: 400; - margin-right: 5px; + .display-name { + margin-left: 15px; + text-align: left; + + strong { + font-size: 15px; + color: $primary-text-color; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; } - &:hover, - &:active, - &:focus { - color: $white; + span { + display: block; + font-size: 14px; + color: $darker-text-color; + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; } } } } -.account-grid-card .controls { - left: auto; - right: 15px; -} - .pagination { padding: 30px 0; text-align: center; @@ -314,289 +171,23 @@ } } -.accounts-grid { - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - background: darken($simple-background-color, 8%); - border-radius: 0 0 4px 4px; - padding: 20px 5px; - padding-bottom: 10px; - overflow: hidden; - display: flex; - flex-wrap: wrap; - z-index: 2; - position: relative; - - &.empty img { - position: absolute; - opacity: 0.2; - height: 200px; - left: 0; - bottom: 0; - pointer-events: none; - } - - @media screen and (max-width: 740px) { - border-radius: 0; - box-shadow: none; - } - - .account-grid-card { - box-sizing: border-box; - width: 335px; - background: $simple-background-color; - border-radius: 4px; - color: $inverted-text-color; - margin: 0 5px 10px; - position: relative; - - @media screen and (max-width: 740px) { - width: calc(100% - 10px); - } - - .account-grid-card__header { - overflow: hidden; - height: 100px; - border-radius: 4px 4px 0 0; - background-color: lighten($inverted-text-color, 4%); - background-size: cover; - background-position: center; - position: relative; - - &::after { - background: rgba(darken($ui-base-color, 8%), 0.5); - display: block; - content: ""; - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - z-index: 1; - } - } - - .account-grid-card__avatar { - box-sizing: border-box; - padding: 15px; - position: absolute; - z-index: 2; - top: 100px - (40px + 2px); - left: -2px; - } - - .avatar { - width: 80px; - height: 80px; - - img { - display: block; - width: 80px; - height: 80px; - border-radius: 80px; - border: 2px solid $simple-background-color; - background: $simple-background-color; - } - } - - .name { - padding: 15px; - padding-top: 10px; - padding-left: 15px + 80px + 15px; - - a { - display: block; - color: $inverted-text-color; - text-decoration: none; - text-overflow: ellipsis; - overflow: hidden; - font-weight: 500; - - &:hover { - .display_name { - text-decoration: underline; - } - } - } - } - - .display_name { - font-size: 16px; - display: block; - text-overflow: ellipsis; - overflow: hidden; - } - - .username { - color: $lighter-text-color; - font-size: 14px; - font-weight: 400; - } - - .account__header__content { - padding: 10px 15px; - padding-top: 15px; - color: $lighter-text-color; - word-wrap: break-word; - overflow: hidden; - text-overflow: ellipsis; - height: 5.5em; - position: relative; - - &::after { - display: block; - content: ""; - width: 100%; - height: 100px; - position: absolute; - bottom: 0; - background: linear-gradient(to bottom, rgba($simple-background-color, 0.01) 0%, rgba($simple-background-color, 1) 100%); - left: 0; - border-radius: 0 0 4px 4px; - pointer-events: none; - } - } - } -} - .nothing-here { - width: 100%; - display: block; + background: $ui-base-color; + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); color: $light-text-color; font-size: 14px; font-weight: 500; text-align: center; - padding: 130px 0; - padding-top: 125px; - margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; cursor: default; -} - -.account-card { border-radius: 4px; - text-align: left; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - background: $simple-background-color; - - &__header { - background: $base-shadow-color; - background-size: cover; - background-position: center center; - height: 90px; - border-radius: 4px 4px 0 0; - } + padding: 20px; + min-height: 30vh; - & > .detailed-status__display-name { - display: block; - overflow: hidden; - display: flex; - align-items: center; - padding: 10px; - - &:last-child { - margin-bottom: 0; - } - - & > div:first-child { - flex: 0 0 auto; - margin-right: 10px; - width: 48px; - height: 48px; - } - - .avatar { - display: block; - border-radius: 4px; - margin: 0; - } - - .display-name { - flex: 1 0 auto; - display: block; - max-width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - cursor: default; - - & > .detailed-status__display-name { - margin-bottom: 0; - } - - strong { - font-weight: 500; - color: $ui-base-color; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - span { - font-size: 14px; - color: $light-text-color; - } - } - - &:hover { - .display-name { - strong { - text-decoration: none; - } - } - } - } - - .counter { - box-sizing: border-box; - flex: 0 0 auto; - color: $light-text-color; - padding: 0 10px; - cursor: default; - text-align: center; - position: relative; - line-height: 24px; - - .counter-label { - font-size: 12px; - display: block; - text-transform: uppercase; - } - - .counter-number { - font-weight: 500; - font-size: 16px; - color: $inverted-text-color; - font-family: 'mastodon-font-display', sans-serif; - } - } -} - -.activity-stream-tabs { - background: $simple-background-color; - border-bottom: 1px solid $ui-secondary-color; - position: relative; - z-index: 2; - - a { - display: inline-block; - padding: 15px; - text-decoration: none; - color: $highlight-text-color; - text-transform: uppercase; - font-weight: 500; - - &:hover, - &:active, - &:focus { - color: lighten($highlight-text-color, 8%); - } - - &.active { - color: $inverted-text-color; - cursor: default; - } + &--under-tabs { + border-radius: 0 0 4px 4px; } } @@ -629,14 +220,14 @@ padding: 0; margin: 15px -15px -15px; border: 0 none; - border-top: 1px solid lighten($ui-base-color, 4%); - border-bottom: 1px solid lighten($ui-base-color, 4%); + border-top: 1px solid lighten($ui-base-color, 12%); + border-bottom: 1px solid lighten($ui-base-color, 12%); font-size: 14px; line-height: 20px; dl { display: flex; - border-bottom: 1px solid lighten($ui-base-color, 4%); + border-bottom: 1px solid lighten($ui-base-color, 12%); } dt, diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index c52e069be..c45a50756 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -1,13 +1,12 @@ body { font-family: 'mastodon-font-sans-serif', sans-serif; - background: $ui-base-color; + background: darken($ui-base-color, 8%); background-size: cover; background-attachment: fixed; font-size: 13px; line-height: 18px; font-weight: 400; color: $primary-text-color; - padding-bottom: 20px; text-rendering: optimizelegibility; font-feature-settings: "kern"; text-size-adjust: none; @@ -52,7 +51,7 @@ body { } &.embed { - background: transparent; + background: lighten($ui-base-color, 4%); margin: 0; padding-bottom: 0; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 846bda893..814739dfc 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -946,6 +946,18 @@ background: lighten($ui-base-color, 4%); padding: 14px 10px; + &--flex { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: flex-start; + + .status__content, + .detailed-status__meta { + flex: 100%; + } + } + .status__content { font-size: 19px; line-height: 24px; diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index ac648c868..c2ff77783 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -118,3 +118,576 @@ margin-left: 8px; } } + +.public-layout { + @media screen and (max-width: $no-gap-breakpoint) { + padding-top: 48px; + } + + .container { + max-width: 960px; + + @media screen and (max-width: $no-gap-breakpoint) { + padding: 0; + } + } + + .header { + background: lighten($ui-base-color, 8%); + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + border-radius: 4px; + height: 48px; + margin: 10px 0; + display: flex; + align-items: stretch; + justify-content: center; + flex-wrap: nowrap; + overflow: hidden; + + @media screen and (max-width: $no-gap-breakpoint) { + position: fixed; + width: 100%; + top: 0; + left: 0; + margin: 0; + border-radius: 0; + box-shadow: none; + z-index: 110; + } + + & > div { + flex: 1 1 33.3%; + min-height: 1px; + } + + .nav-left { + display: flex; + align-items: stretch; + justify-content: flex-start; + flex-wrap: nowrap; + } + + .nav-center { + display: flex; + align-items: stretch; + justify-content: center; + flex-wrap: nowrap; + } + + .nav-right { + display: flex; + align-items: stretch; + justify-content: flex-end; + flex-wrap: nowrap; + } + + .brand { + display: block; + padding: 15px; + + img { + display: block; + height: 18px; + width: auto; + position: relative; + bottom: -2px; + + @media screen and (max-width: $no-gap-breakpoint) { + height: 20px; + } + } + + &:hover, + &:focus, + &:active { + background: lighten($ui-base-color, 12%); + } + } + + .nav-link { + display: flex; + align-items: center; + padding: 0 1rem; + font-size: 12px; + font-weight: 500; + text-decoration: none; + color: $darker-text-color; + white-space: nowrap; + text-align: center; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + color: $primary-text-color; + } + } + + .nav-button { + background: lighten($ui-base-color, 16%); + margin: 8px; + margin-left: 0; + border-radius: 4px; + + &:hover, + &:focus, + &:active { + text-decoration: none; + background: lighten($ui-base-color, 20%); + } + } + } + + $no-columns-breakpoint: 600px; + + .grid { + display: grid; + grid-gap: 10px; + grid-template-columns: minmax(300px, 3fr) minmax(298px, 1fr); + grid-auto-columns: 25%; + grid-auto-rows: max-content; + + .column-0 { + grid-row: 1; + grid-column: 1; + } + + .column-1 { + grid-row: 1; + grid-column: 2; + } + + @media screen and (max-width: $no-columns-breakpoint) { + grid-template-columns: 100%; + grid-gap: 0; + + .column-1 { + display: none; + } + } + } + + .public-account-header { + overflow: hidden; + margin-bottom: 10px; + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + + &__image { + border-radius: 4px 4px 0 0; + overflow: hidden; + height: 300px; + position: relative; + background: darken($ui-base-color, 12%); + + &::after { + content: ""; + display: block; + position: absolute; + width: 100%; + height: 100%; + box-shadow: inset 0 -1px 1px 1px rgba($base-shadow-color, 0.15); + top: 0; + left: 0; + } + + img { + object-fit: cover; + display: block; + width: 100%; + height: 100%; + margin: 0; + border-radius: 4px 4px 0 0; + } + + @media screen and (max-width: 600px) { + height: 200px; + } + } + + @media screen and (max-width: $no-gap-breakpoint) { + margin-bottom: 0; + box-shadow: none; + + &__image::after { + display: none; + } + + &__image, + &__image img { + border-radius: 0; + } + } + + &__bar { + position: relative; + margin-top: -80px; + display: flex; + justify-content: flex-start; + + &::before { + content: ""; + display: block; + background: lighten($ui-base-color, 4%); + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60px; + border-radius: 0 0 4px 4px; + z-index: -1; + } + + .avatar { + display: block; + width: 120px; + height: 120px; + padding-left: 20px - 4px; + flex: 0 0 auto; + + img { + display: block; + width: 100%; + height: 100%; + margin: 0; + border-radius: 50%; + border: 4px solid lighten($ui-base-color, 4%); + background: darken($ui-base-color, 8%); + } + } + + @media screen and (max-width: 600px) { + margin-top: 0; + background: lighten($ui-base-color, 4%); + border-radius: 0 0 4px 4px; + padding: 5px; + + &::before { + display: none; + } + + .avatar { + width: 48px; + height: 48px; + padding: 7px 0; + padding-left: 10px; + + img { + border: 0; + border-radius: 4px; + } + + @media screen and (max-width: 360px) { + display: none; + } + } + } + + @media screen and (max-width: $no-gap-breakpoint) { + border-radius: 0; + } + + @media screen and (max-width: $no-columns-breakpoint) { + flex-wrap: wrap; + } + } + + &__tabs { + flex: 1 1 auto; + margin-left: 20px; + + &__name { + padding-top: 20px; + padding-bottom: 8px; + + h1 { + font-size: 20px; + line-height: 18px * 1.5; + color: $primary-text-color; + font-weight: 500; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + text-shadow: 1px 1px 1px $base-shadow-color; + + small { + display: block; + font-size: 14px; + color: $primary-text-color; + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + @media screen and (max-width: 600px) { + margin-left: 15px; + display: flex; + justify-content: space-between; + align-items: center; + + &__name { + padding-top: 0; + padding-bottom: 0; + + h1 { + font-size: 16px; + line-height: 24px; + text-shadow: none; + + small { + color: $darker-text-color; + } + } + } + } + + &__tabs { + display: flex; + justify-content: flex-start; + align-items: stretch; + height: 58px; + + .details-counters { + display: flex; + flex-direction: row; + min-width: 300px; + } + + @media screen and (max-width: $no-columns-breakpoint) { + .details-counters { + display: none; + } + } + + .counter { + width: 33.3%; + box-sizing: border-box; + flex: 0 0 auto; + color: $darker-text-color; + padding: 10px; + border-right: 1px solid lighten($ui-base-color, 4%); + cursor: default; + text-align: center; + position: relative; + + a { + display: block; + } + + &:last-child { + border-right: 0; + } + + &::after { + display: block; + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + border-bottom: 4px solid $ui-primary-color; + opacity: 0.5; + transition: all 400ms ease; + } + + &.active { + &::after { + border-bottom: 4px solid $highlight-text-color; + opacity: 1; + } + } + + &:hover { + &::after { + opacity: 1; + transition-duration: 100ms; + } + } + + a { + text-decoration: none; + color: inherit; + } + + .counter-label { + font-size: 12px; + display: block; + } + + .counter-number { + font-weight: 500; + font-size: 18px; + margin-bottom: 5px; + color: $primary-text-color; + font-family: 'mastodon-font-display', sans-serif; + } + } + + .spacer { + flex: 1 1 auto; + height: 1px; + } + + &__buttons { + padding: 7px 8px; + } + } + } + + &__extra { + display: none; + margin-top: 4px; + + .public-account-bio { + border-radius: 0; + box-shadow: none; + background: transparent; + margin: 0 -5px; + + .account__header__fields { + border-top: 1px solid lighten($ui-base-color, 12%); + } + + .roles { + display: none; + } + } + + &__links { + margin-top: -15px; + font-size: 14px; + color: $darker-text-color; + + a { + display: inline-block; + color: $darker-text-color; + text-decoration: none; + padding: 15px; + + strong { + font-weight: 700; + color: $primary-text-color; + } + } + } + + @media screen and (max-width: $no-columns-breakpoint) { + display: block; + flex: 100%; + } + } + } + + .account__section-headline { + border-radius: 4px 4px 0 0; + + @media screen and (max-width: $no-gap-breakpoint) { + border-radius: 0; + } + } + + .detailed-status__meta { + margin-top: 25px; + } + + .public-account-bio { + background: lighten($ui-base-color, 8%); + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + border-radius: 4px; + overflow: hidden; + margin-bottom: 10px; + + @media screen and (max-width: $no-gap-breakpoint) { + box-shadow: none; + margin-bottom: 0; + border-radius: 0; + } + + .account__header__fields { + margin: 0; + border-top: 0; + + a { + color: lighten($ui-highlight-color, 8%); + } + } + + .account__header__content { + padding: 20px; + padding-bottom: 0; + color: $primary-text-color; + } + + &__extra, + .roles { + padding: 20px; + font-size: 14px; + color: $darker-text-color; + } + + .roles { + padding-bottom: 0; + } + } + + .static-icon-button { + color: $action-button-color; + font-size: 18px; + + & > span { + font-size: 14px; + font-weight: 500; + } + } + + .card-grid { + display: flex; + flex-wrap: wrap; + min-width: 100%; + margin: 0 -5px; + + & > div { + box-sizing: border-box; + flex: 1 0 auto; + width: 300px; + padding: 0 5px; + margin-bottom: 10px; + max-width: 33.333%; + + @media screen and (max-width: 900px) { + max-width: 50%; + } + + @media screen and (max-width: 600px) { + max-width: 100%; + } + } + + @media screen and (max-width: $no-gap-breakpoint) { + margin: 0; + border-top: 1px solid lighten($ui-base-color, 8%); + + & > div { + width: 100%; + padding: 0; + margin-bottom: 0; + border-bottom: 1px solid lighten($ui-base-color, 8%); + + &:last-child { + border-bottom: 0; + } + + .card__bar { + background: $ui-base-color; + + &:hover, + &:active, + &:focus { + background: lighten($ui-base-color, 4%); + } + } + } + } + } +} diff --git a/app/javascript/styles/mastodon/footer.scss b/app/javascript/styles/mastodon/footer.scss index 81eb1ce2d..4d75477e0 100644 --- a/app/javascript/styles/mastodon/footer.scss +++ b/app/javascript/styles/mastodon/footer.scss @@ -1,39 +1,140 @@ -.footer { - text-align: center; - margin-top: 30px; - padding-bottom: 60px; - font-size: 12px; - color: $darker-text-color; - - .footer__domain { - font-weight: 500; - - a { - color: inherit; - text-decoration: none; +.public-layout { + .footer { + text-align: left; + padding-top: 20px; + padding-bottom: 60px; + font-size: 12px; + color: lighten($ui-base-color, 34%); + + @media screen and (max-width: $no-gap-breakpoint) { + padding-left: 20px; + padding-right: 20px; } - } - .powered-by, - .single-user-login { - font-weight: 400; + .grid { + display: grid; + grid-gap: 10px; + grid-template-columns: 1fr 1fr 2fr 1fr 1fr; + + .column-0 { + grid-column: 1; + grid-row: 1; + min-width: 0; + } + + .column-1 { + grid-column: 2; + grid-row: 1; + min-width: 0; + } + + .column-2 { + grid-column: 3; + grid-row: 1; + min-width: 0; + text-align: center; + + h4 a { + color: lighten($ui-base-color, 34%); + } + } + + .column-3 { + grid-column: 4; + grid-row: 1; + min-width: 0; + } + + .column-4 { + grid-column: 5; + grid-row: 1; + min-width: 0; + } + + @media screen and (max-width: 690px) { + grid-template-columns: 1fr 2fr 1fr; + + .column-0, + .column-1 { + grid-column: 1; + } + + .column-1 { + grid-row: 2; + } + + .column-2 { + grid-column: 2; + } - a { - color: inherit; - text-decoration: underline; - font-weight: 500; + .column-3, + .column-4 { + grid-column: 3; + } - &:hover { + .column-4 { + grid-row: 2; + } + } + + @media screen and (max-width: 600px) { + .column-1 { + display: block; + } + } + + @media screen and (max-width: $no-gap-breakpoint) { + .column-0, + .column-1, + .column-3, + .column-4 { + display: none; + } + } + } + + h4 { + text-transform: uppercase; + font-weight: 700; + margin-bottom: 8px; + color: $darker-text-color; + + a { + color: inherit; text-decoration: none; } } - img { - margin: 0 4px; - position: relative; - bottom: -1px; - height: 18px; - vertical-align: top; + ul a { + text-decoration: none; + color: lighten($ui-base-color, 34%); + + &:hover, + &:active, + &:focus { + text-decoration: underline; + } + } + + .brand { + svg { + display: block; + height: 36px; + width: auto; + margin: 0 auto; + + path { + fill: lighten($ui-base-color, 34%); + } + } + + &:hover, + &:focus, + &:active { + svg path { + fill: lighten($ui-base-color, 38%); + } + } } } } diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss deleted file mode 100644 index 86614b89b..000000000 --- a/app/javascript/styles/mastodon/landing_strip.scss +++ /dev/null @@ -1,111 +0,0 @@ -.landing-strip, -.memoriam-strip { - background: rgba(darken($ui-base-color, 7%), 0.8); - color: $darker-text-color; - font-weight: 400; - padding: 14px; - border-radius: 4px; - margin-bottom: 20px; - display: flex; - align-items: center; - - strong, - a { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - a { - color: inherit; - text-decoration: underline; - } - - .logo { - width: 30px; - height: 30px; - flex: 0 0 auto; - margin-right: 15px; - } - - @media screen and (max-width: 740px) { - margin-bottom: 0; - } -} - -.memoriam-strip { - background: rgba($base-shadow-color, 0.7); -} - -.moved-strip { - padding: 14px; - border-radius: 4px; - background: rgba(darken($ui-base-color, 7%), 0.8); - color: $secondary-text-color; - font-weight: 400; - margin-bottom: 20px; - - strong, - a { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - a { - color: inherit; - text-decoration: underline; - - &.mention { - text-decoration: none; - - span { - text-decoration: none; - } - - &:focus, - &:hover, - &:active { - text-decoration: none; - - span { - text-decoration: underline; - } - } - } - } - - &__message { - margin-bottom: 15px; - - .fa { - margin-right: 5px; - color: $darker-text-color; - } - } - - &__card { - .detailed-status__display-avatar { - position: relative; - cursor: pointer; - } - - .detailed-status__display-name { - margin-bottom: 0; - text-decoration: none; - - span { - color: $highlight-text-color; - font-weight: 400; - } - } - } -} diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index f4d6e237f..9e2aa720c 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -1,367 +1,145 @@ .activity-stream { - clear: both; box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + border-radius: 4px; + overflow: hidden; + margin-bottom: 10px; + + @media screen and (max-width: $no-gap-breakpoint) { + margin-bottom: 0; + border-radius: 0; + box-shadow: none; + } + + &--headless { + border-radius: 0; + margin: 0; + box-shadow: none; + + .detailed-status, + .status { + border-radius: 0 !important; + } + } div[data-component] { width: 100%; } .entry { - background: $simple-background-color; + background: $ui-base-color; - .detailed-status.light, - .status.light, - .more.light { - border-bottom: 1px solid $ui-secondary-color; + .detailed-status, + .status, + .load-more { animation: none; } &:last-child { - &, - .detailed-status.light, - .status.light { + .detailed-status, + .status { border-bottom: 0; border-radius: 0 0 4px 4px; } } &:first-child { - &, - .detailed-status.light, - .status.light { + .detailed-status, + .status { border-radius: 4px 4px 0 0; } &:last-child { - &, - .detailed-status.light, - .status.light { + .detailed-status, + .status { border-radius: 4px; } } } @media screen and (max-width: 740px) { - &, - .detailed-status.light, - .status.light { + .detailed-status, + .status { border-radius: 0 !important; } } } +} - &.with-header { - .entry { - &:first-child { - &, - .detailed-status.light, - .status.light { - border-radius: 0; - } - - &:last-child { - &, - .detailed-status.light, - .status.light { - border-radius: 0 0 4px 4px; - } - } - } - } - } - - .media-gallery__gifv__label { - bottom: 9px; - } - - .status.light { - padding: 14px 14px 14px (48px + 14px * 2); - position: relative; - min-height: 48px; - cursor: default; - - .status__header { - font-size: 15px; - - .status__meta { - float: right; - font-size: 14px; - - .status__relative-time { - color: $lighter-text-color; - } - } - } - - .status__display-name { - display: block; - max-width: 100%; - padding-right: 25px; - color: $inverted-text-color; - } - - .status__avatar { - position: absolute; - left: 14px; - top: 14px; - width: 48px; - height: 48px; - - & > div { - width: 48px; - height: 48px; - } - - img { - display: block; - border-radius: 4px; - } - } - - .display-name { - display: block; - max-width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - strong { - font-weight: 500; - color: $inverted-text-color; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - span { - font-size: 14px; - color: $light-text-color; - } - } - - .status__content { - color: $inverted-text-color; - - a { - color: $highlight-text-color; - } - - a.status__content__spoiler-link { - color: $primary-text-color; - background: $ui-base-color; - - &:hover { - background: lighten($ui-base-color, 8%); - } - } - } - } - - .detailed-status.light { - padding: 14px; - background: $simple-background-color; - cursor: default; - - .detailed-status__display-name { - display: block; - overflow: hidden; - margin-bottom: 15px; - - & > div { - float: left; - margin-right: 10px; - } - - .display-name { - display: block; - max-width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - strong { - font-weight: 500; - color: $inverted-text-color; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - span { - font-size: 14px; - color: $light-text-color; - } - } - } - - .avatar { - width: 48px; - height: 48px; - - img { - display: block; - border-radius: 4px; - } - } - - .status__content { - color: $inverted-text-color; - - a { - color: $highlight-text-color; - } - - a.status__content__spoiler-link { - color: $primary-text-color; - background: $ui-base-color; - - &:hover { - background: lighten($ui-base-color, 8%); - } - } - } - - .detailed-status__meta { - margin-top: 15px; - color: $light-text-color; - font-size: 14px; - line-height: 18px; - - a { - color: inherit; - } - - span > span { - font-weight: 500; - font-size: 12px; - margin-left: 6px; - display: inline-block; - } - } - - .status-card { - border-color: lighten($ui-secondary-color, 4%); - color: $lighter-text-color; - - &:hover { - background: lighten($ui-secondary-color, 4%); - } - } - - .status-card__title, - .status-card__description { - color: $inverted-text-color; - } - - .status-card__image { - background: $ui-secondary-color; - } - } - - .media-spoiler { - background: $ui-base-color; - color: $darker-text-color; - } +.button.logo-button { + flex: 0 auto; + font-size: 14px; + background: $ui-highlight-color; + color: $primary-text-color; + text-transform: none; + line-height: 36px; + height: auto; + padding: 3px 15px; + border: 0; - .pre-header { - padding: 14px 0; - padding-left: (48px + 14px * 2); - padding-bottom: 0; - margin-bottom: -4px; - color: $light-text-color; - font-size: 14px; - position: relative; + svg { + width: 20px; + height: auto; + vertical-align: middle; + margin-right: 5px; - .pre-header__icon { - position: absolute; - left: (48px + 14px * 2 - 30px); + path:first-child { + fill: $primary-text-color; } - .status__display-name.muted strong { - color: $light-text-color; + path:last-child { + fill: $ui-highlight-color; } } - .open-in-web-link { - text-decoration: none; + &:active, + &:focus, + &:hover { + background: lighten($ui-highlight-color, 10%); - &:hover { - text-decoration: underline; + svg path:last-child { + fill: lighten($ui-highlight-color, 10%); } } - .more { - color: $darker-text-color; - display: block; - padding: 14px; - text-align: center; - - &:not(:hover) { - text-decoration: none; + @media screen and (max-width: $no-gap-breakpoint) { + svg { + display: none; } } } -.embed { - .activity-stream { - box-shadow: none; +.embed, +.public-layout { + .detailed-status { + padding: 15px; } -} -.entry { - .detailed-status.light { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - align-items: flex-start; + .status { + padding: 15px 15px 15px (48px + 15px * 2); + min-height: 48px + 2px; - .detailed-status__display-name { - flex: 1; - margin: 0 5px 15px 0; + &__avatar { + left: 15px; + top: 17px; } - .button.button-secondary.logo-button { - flex: 0 auto; - font-size: 14px; - background: $ui-highlight-color; - color: $primary-text-color; - border: 0; - - svg { - width: 20px; - height: auto; - vertical-align: middle; - margin-right: 5px; - - path:first-child { - fill: $primary-text-color; - } - - path:last-child { - fill: $ui-highlight-color; - } - } + &__content { + padding-top: 5px; + } - &:active, - &:focus, - &:hover { - background: lighten($ui-highlight-color, 10%); + &__prepend { + margin-left: 48px + 15px * 2; + padding-top: 15px; + } - svg path:last-child { - fill: lighten($ui-highlight-color, 10%); - } - } + &__prepend-icon-wrapper { + left: -32px; } - .status__content, - .detailed-status__meta { - flex: 100%; + .media-gallery, + &__action-bar, + .video-player { + margin-top: 10px; } } } diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index 40aeb4afc..009f0a3c9 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -46,3 +46,5 @@ $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; $media-modal-media-max-width: 100%; // put margins on top and bottom of image to avoid the screen covered by image. $media-modal-media-max-height: 80%; + +$no-gap-breakpoint: 415px; diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss new file mode 100644 index 000000000..9324533bd --- /dev/null +++ b/app/javascript/styles/mastodon/widgets.scss @@ -0,0 +1,123 @@ +.hero-widget { + margin-bottom: 10px; + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + + &__img { + width: 100%; + height: 167px; + position: relative; + overflow: hidden; + border-radius: 4px 4px 0 0; + background: $base-shadow-color; + + img { + object-fit: cover; + display: block; + width: 100%; + height: 100%; + margin: 0; + border-radius: 4px 4px 0 0; + } + } + + &__text { + background: $ui-base-color; + padding: 20px; + border-radius: 0 0 4px 4px; + font-size: 14px; + color: $darker-text-color; + } + + @media screen and (max-width: $no-gap-breakpoint) { + display: none; + } +} + +.moved-account-widget { + padding: 15px; + padding-bottom: 20px; + border-radius: 4px; + background: $ui-base-color; + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + color: $secondary-text-color; + font-weight: 400; + margin-bottom: 10px; + + strong, + a { + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + a { + color: inherit; + text-decoration: underline; + + &.mention { + text-decoration: none; + + span { + text-decoration: none; + } + + &:focus, + &:hover, + &:active { + text-decoration: none; + + span { + text-decoration: underline; + } + } + } + } + + &__message { + margin-bottom: 15px; + + .fa { + margin-right: 5px; + color: $darker-text-color; + } + } + + &__card { + .detailed-status__display-avatar { + position: relative; + cursor: pointer; + } + + .detailed-status__display-name { + margin-bottom: 0; + text-decoration: none; + + span { + font-weight: 400; + } + } + } +} + +.memoriam-widget { + padding: 20px; + border-radius: 4px; + background: $base-shadow-color; + box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + font-size: 14px; + color: $darker-text-color; + margin-bottom: 10px; +} + +.moved-account-widget, +.memoriam-widget { + @media screen and (max-width: $no-gap-breakpoint) { + margin-bottom: 0; + box-shadow: none; + border-radius: 0; + } +} diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account_header.rb index ef40b8126..067e166eb 100644 --- a/app/models/concerns/account_header.rb +++ b/app/models/concerns/account_header.rb @@ -5,11 +5,12 @@ module AccountHeader IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze LIMIT = 2.megabytes + MAX_PIXELS = 750_000 # 1500x500px class_methods do def header_styles(file) - styles = { original: { geometry: '700x335#', file_geometry_parser: FastGeometryParser } } - styles[:static] = { geometry: '700x335#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif' + styles = { original: { pixels: MAX_PIXELS, file_geometry_parser: FastGeometryParser } } + styles[:static] = { format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif' styles end diff --git a/app/views/accounts/_bio.html.haml b/app/views/accounts/_bio.html.haml new file mode 100644 index 000000000..c9a4d8f1b --- /dev/null +++ b/app/views/accounts/_bio.html.haml @@ -0,0 +1,15 @@ +.public-account-bio + - unless account.fields.empty? + .account__header__fields + - account.fields.each do |field| + %dl + %dt.emojify{ title: field.name }= field.name + %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true) + + = account_badge(account) + + - if account.note.present? + .account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) + + .public-account-bio__extra + = t 'accounts.joined', date: l(account.created_at, format: :month) diff --git a/app/views/accounts/_follow_button.html.haml b/app/views/accounts/_follow_button.html.haml deleted file mode 100644 index 558ced010..000000000 --- a/app/views/accounts/_follow_button.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -- relationships ||= nil - -- unless account.memorial? || account.moved? - - if user_signed_in? - - requested = relationships ? relationships.requested[account.id].present? : current_account.requested?(account) - - following = relationships ? relationships.following[account.id].present? : current_account.following?(account) - - - if user_signed_in? && current_account.id != account.id && !requested - .controls - - if following - = link_to (account.local? ? account_unfollow_path(account) : remote_unfollow_path(acct: account.acct)), data: { method: :post }, class: 'icon-button' do - = fa_icon 'user-times' - = t('accounts.unfollow') - - else - = link_to (account.local? ? account_follow_path(account) : authorize_follow_path(acct: account.acct)), data: { method: :post }, class: 'icon-button' do - = fa_icon 'user-plus' - = t('accounts.follow') - - elsif user_signed_in? && current_account.id == account.id - .controls - = link_to settings_profile_url, class: 'icon-button' do - = fa_icon 'pencil' - = t('settings.edit_profile') - - elsif !user_signed_in? - .controls - .remote-follow - = link_to (account.local? ? account_remote_follow_path(account) : "web+mastodon://follow?uri=#{account.uri}"), class: 'icon-button' do - = fa_icon 'user-plus' - = t('accounts.remote_follow') diff --git a/app/views/accounts/_follow_grid.html.haml b/app/views/accounts/_follow_grid.html.haml deleted file mode 100644 index fdcef84be..000000000 --- a/app/views/accounts/_follow_grid.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -.accounts-grid{ class: accounts.empty? ? 'empty' : '' } - - if accounts.empty? - = image_tag asset_pack_path('elephant_ui_greeting.svg'), alt: '', role: 'presentational' - = render partial: 'accounts/nothing_here' - - else - = render partial: 'accounts/grid_card', collection: accounts, as: :account, cached: !user_signed_in? - -= paginate follows diff --git a/app/views/accounts/_follow_grid_hidden.html.haml b/app/views/accounts/_follow_grid_hidden.html.haml deleted file mode 100644 index e970350e6..000000000 --- a/app/views/accounts/_follow_grid_hidden.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -.accounts-grid.empty - = image_tag asset_pack_path('elephant_ui_greeting.svg'), alt: '', role: 'presentational' - %p.nothing-here= t('accounts.network_hidden') diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml deleted file mode 100644 index a59ed128e..000000000 --- a/app/views/accounts/_grid_card.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -.account-grid-card - .account-grid-card__header{ style: "background-image: url(#{account.header.url(:original)})" } - = render 'accounts/follow_button', account: account, relationships: @relationships - .account-grid-card__avatar - .avatar= image_tag account.avatar.url(:original) - .name - = link_to TagManager.instance.url_for(account) do - %span.display_name.emojify= display_name(account, custom_emojify: true) - %span.username - @#{account.local? ? account.local_username_and_domain : account.acct} - = fa_icon('lock') if account.locked? - .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account) diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index 4098d6778..e343be820 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -1,51 +1,43 @@ -.card.h-card.p-author{ style: "background-image: url(#{account.header.url(:original)})" } - .card__illustration - = render 'accounts/follow_button', account: account - .avatar= image_tag account.avatar.url(:original), class: 'u-photo' +.public-account-header + .public-account-header__image + = image_tag account.header.url, class: 'parallax' + .public-account-header__bar + = link_to short_account_url(account), class: 'avatar' do + = image_tag account.avatar.url + .public-account-header__tabs + .public-account-header__tabs__name + %h1 + = display_name(account) + %small + = acct(account) + = fa_icon('lock') if account.locked? + .public-account-header__tabs__tabs + .details-counters + .counter{ class: active_nav_class(short_account_url(account)) } + = link_to short_account_url(account), class: 'u-url u-uid' do + %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true + %span.counter-label= t('accounts.posts') - .card__bio - %h1.name - %span.p-name.emojify= display_name(account, custom_emojify: true) - %small< - %span>< @#{account.local_username_and_domain} - = fa_icon('lock') if account.locked? + .counter{ class: active_nav_class(account_following_index_url(account)) } + = link_to account_following_index_url(account) do + %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true + %span.counter-label= t('accounts.following') - - if account.bot? - .roles - .account-role.bot - = t 'accounts.roles.bot' - - elsif Setting.show_staff_badge - - if account.user_admin? - .roles - .account-role.admin - = t 'accounts.roles.admin' - - elsif account.user_moderator? - .roles - .account-role.moderator - = t 'accounts.roles.moderator' + .counter{ class: active_nav_class(account_followers_url(account)) } + = link_to account_followers_url(account) do + %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true + %span.counter-label= t('accounts.followers') + .spacer + .public-account-header__tabs__tabs__buttons + = account_action_button(account) - .bio - .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) + .public-account-header__extra + = render 'accounts/bio', account: account - - unless account.fields.empty? - .account__header__fields - - account.fields.each do |field| - %dl - %dt.emojify{ title: field.name }= field.name - %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true) - - .details-counters - .counter{ class: active_nav_class(short_account_url(account)) } - = link_to short_account_url(account), class: 'u-url u-uid' do - %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true - %span.counter-label= t('accounts.posts') - - .counter{ class: active_nav_class(account_following_index_url(account)) } + .public-account-header__extra__links = link_to account_following_index_url(account) do - %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true - %span.counter-label= t('accounts.following') - - .counter{ class: active_nav_class(account_followers_url(account)) } + %strong= number_to_human account.following_count, strip_insignificant_zeros: true + = t('accounts.following') = link_to account_followers_url(account) do - %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true - %span.counter-label= t('accounts.followers') + %strong= number_to_human account.followers_count, strip_insignificant_zeros: true + = t('accounts.followers') diff --git a/app/views/accounts/_moved_strip.html.haml b/app/views/accounts/_moved.html.haml index ae18c6dc7..f99328dbd 100644 --- a/app/views/accounts/_moved_strip.html.haml +++ b/app/views/accounts/_moved.html.haml @@ -1,11 +1,11 @@ - moved_to_account = account.moved_to_account -.moved-strip - .moved-strip__message +.moved-account-widget + .moved-account-widget__message = fa_icon 'suitcase' - = t('accounts.moved_html', name: content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention')) + = t('accounts.moved_html', name: content_tag(:bdi, content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify)), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention')) - .moved-strip__card + .moved-account-widget__card = link_to TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do .detailed-status__display-avatar .account__avatar-overlay @@ -13,5 +13,6 @@ .account__avatar-overlay-overlay{ style: "background-image: url('#{account.avatar.url(:original)}')" } %span.display-name - %strong.emojify= display_name(moved_to_account, custom_emojify: true) + %bdi + %strong.emojify= display_name(moved_to_account, custom_emojify: true) %span @#{moved_to_account.acct} diff --git a/app/views/accounts/_nothing_here.html.haml b/app/views/accounts/_nothing_here.html.haml deleted file mode 100644 index 0c6dc1168..000000000 --- a/app/views/accounts/_nothing_here.html.haml +++ /dev/null @@ -1 +0,0 @@ -%p.nothing-here= t('accounts.nothing_here') diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index cfdd3a945..b30755d94 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -20,36 +20,39 @@ = opengraph 'og:type', 'profile' = render 'og', account: @account, url: short_account_url(@account, only_path: false) -- if @account.memorial? - .memoriam-strip= t('in_memoriam_html') -- elsif @account.moved? - = render partial: 'moved_strip', locals: { account: @account } -- elsif show_landing_strip? - = render partial: 'shared/landing_strip', locals: { account: @account } - -.h-feed - %data.p-name{ value: "#{@account.username} on #{site_hostname}" }/ - - = render 'header', account: @account - - .activity-stream-tabs - = active_link_to t('accounts.posts'), short_account_url(@account) - = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account) - = active_link_to t('accounts.media'), short_account_media_url(@account) - - - if @statuses.empty? - .accounts-grid - = render 'nothing_here' - - else - .activity-stream.with-header - - if params[:page].to_i.zero? - = render partial: 'stream_entries/status', collection: @pinned_statuses, as: :status, locals: { pinned: true } - - = render partial: 'stream_entries/status', collection: @statuses, as: :status - - - if @newer_url || @older_url - .pagination - - if @older_url - = link_to safe_join([fa_icon('chevron-left'), t('pagination.older')], ' '), @older_url, class: 'older', rel: 'next' - - if @newer_url - = link_to safe_join([t('pagination.newer'), fa_icon('chevron-right')], ' '), @newer_url, class: 'newer', rel: 'prev' + += render 'header', account: @account, with_bio: true + +.grid + .column-0 + .h-feed + %data.p-name{ value: "#{@account.username} on #{site_hostname}" }/ + + .account__section-headline + = active_link_to t('accounts.posts'), short_account_url(@account) + = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account) + = active_link_to t('accounts.media'), short_account_media_url(@account) + + - if @statuses.empty? + = nothing_here 'nothing-here--under-tabs' + - else + .activity-stream + - if params[:page].to_i.zero? + = render partial: 'stream_entries/status', collection: @pinned_statuses, as: :status, locals: { pinned: true } + + - if @newer_url + .entry= link_to_more @newer_url + + = render partial: 'stream_entries/status', collection: @statuses, as: :status + + - if @older_url + .entry= link_to_more @older_url + + .column-1 + - if @account.memorial? + .memoriam-widget= t('in_memoriam_html') + - elsif @account.moved? + = render 'moved', account: @account + + = render 'bio', account: @account + = render 'application/sidebar' diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml new file mode 100644 index 000000000..9cf8f8ff2 --- /dev/null +++ b/app/views/application/_card.html.haml @@ -0,0 +1,16 @@ +- account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account) + +.card.h-card + = link_to account_url, target: '_blank', rel: 'noopener' do + .card__img + = image_tag account.header.url, alt: '' + .card__bar + .avatar + = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' + + .display-name + %bdi + %strong.emojify.p-name= display_name(account, custom_emojify: true) + %span + = acct(account) + = fa_icon('lock') if account.locked? diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml new file mode 100644 index 000000000..3d8832bb4 --- /dev/null +++ b/app/views/application/_sidebar.html.haml @@ -0,0 +1,6 @@ +.hero-widget + .hero-widget__img + = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('preview.jpg'), alt: @instance_presenter.site_title + + .hero-widget__text + %p= @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname) diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 0fac8e10d..200ed42de 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -10,7 +10,7 @@ - if @invite.present? && @invite.autofollow? .fields-group{ style: 'margin-bottom: 30px' } %p.hint{ style: 'text-align: center' }= t('invites.invited_by') - = render 'authorize_follows/card', account: @invite.user.account + = render 'application/card', account: @invite.user.account = f.simple_fields_for :account do |ff| .input-with-append diff --git a/app/views/authorize_follows/_card.html.haml b/app/views/authorize_follows/_card.html.haml deleted file mode 100644 index edc03131f..000000000 --- a/app/views/authorize_follows/_card.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -.account-card - .account-card__header{ style: "background-image: url(#{account.header.url(:original)})" } - .detailed-status__display-name - %div - = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar' - - %span.display-name - - account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account) - = link_to account_url, class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do - %strong.emojify= display_name(account, custom_emojify: true) - %span @#{account.acct} - - .counter - %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true - %span.counter-label= t('accounts.posts') - - .counter - %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true - %span.counter-label= t('accounts.following') - - .counter - %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true - %span.counter-label= t('accounts.followers') diff --git a/app/views/authorize_follows/show.html.haml b/app/views/authorize_follows/show.html.haml index a1fd01dd6..90e65b34f 100644 --- a/app/views/authorize_follows/show.html.haml +++ b/app/views/authorize_follows/show.html.haml @@ -3,7 +3,7 @@ .form-container .follow-prompt - = render 'card', account: @account + = render 'application/card', account: @account - if current_account.following?(@account) .flash-message diff --git a/app/views/authorize_follows/success.html.haml b/app/views/authorize_follows/success.html.haml index fa59b24b8..cf9cb50ea 100644 --- a/app/views/authorize_follows/success.html.haml +++ b/app/views/authorize_follows/success.html.haml @@ -8,6 +8,6 @@ - else %h2= t('authorize_follow.following') - = render 'card', account: @account + = render 'application/card', account: @account = render 'post_follow_actions' diff --git a/app/views/follower_accounts/index.html.haml b/app/views/follower_accounts/index.html.haml index 65af81a5b..31dab68bf 100644 --- a/app/views/follower_accounts/index.html.haml +++ b/app/views/follower_accounts/index.html.haml @@ -8,6 +8,11 @@ = render 'accounts/header', account: @account - if @account.user_hides_network? - = render 'accounts/follow_grid_hidden' + .nothing-here= t('accounts.network_hidden') +- elsif @follows.empty? + = nothing_here - else - = render 'accounts/follow_grid', follows: @follows, accounts: @follows.map(&:account) + .card-grid + = render partial: 'application/card', collection: @follows.map(&:account), as: :account + + = paginate @follows diff --git a/app/views/following_accounts/index.html.haml b/app/views/following_accounts/index.html.haml index 8fd95a0b4..8b49b529b 100644 --- a/app/views/following_accounts/index.html.haml +++ b/app/views/following_accounts/index.html.haml @@ -8,6 +8,11 @@ = render 'accounts/header', account: @account - if @account.user_hides_network? - = render 'accounts/follow_grid_hidden' + .nothing-here= t('accounts.network_hidden') +- elsif @follows.empty? + = nothing_here - else - = render 'accounts/follow_grid', follows: @follows, accounts: @follows.map(&:target_account) + .card-grid + = render partial: 'application/card', collection: @follows.map(&:target_account), as: :account + + = paginate @follows diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index 600290297..f9d808bed 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -2,16 +2,49 @@ = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - content_for :content do - .container-alt= yield - .footer - - if !user_signed_in? && single_user_mode? - %span.single-user-login - = link_to t('auth.login'), new_user_session_path - — - %span.footer__domain= link_to site_hostname, about_path - - else - %span.footer__domain= link_to site_hostname, root_path - %span.powered-by - != t('generic.powered_by', link: link_to('https://joinmastodon.org') { image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon' }) + .public-layout + .container + %nav.header + .nav-left + = link_to root_url, class: 'brand' do + = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon' + .nav-center + .nav-right + - if user_signed_in? + = link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn' + - else + = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button' + = link_to t('auth.register'), new_user_registration_path, class: 'webapp-btn nav-link nav-button' + + .container= yield + + .container + .footer + .grid + .column-0 + %h4= t 'footer.resources' + %ul + %li= link_to t('about.terms'), terms_path + %li= link_to t('about.privacy_policy'), terms_path + .column-1 + %h4= t 'footer.developers' + %ul + %li= link_to t('about.documentation'), 'https://github.com/tootsuite/documentation' + %li= link_to t('about.api'), 'https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md' + .column-2 + %h4= link_to t('about.what_is_mastodon'), 'https://joinmastodon.org/' + + = link_to root_url, class: 'brand' do + = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg') + .column-3 + %h4= site_hostname + %ul + %li= link_to t('about.about_this'), about_more_path + %li= "v#{Mastodon::Version.to_s}" + .column-4 + %h4= t 'footer.more' + %ul + %li= link_to t('about.source_code'), Mastodon::Version.source_url + %li= link_to 'joinmastodon.org', 'https://joinmastodon.org' = render template: 'layouts/application' diff --git a/app/views/remote_follow/new.html.haml b/app/views/remote_follow/new.html.haml index 9b22fda5f..9b679015f 100644 --- a/app/views/remote_follow/new.html.haml +++ b/app/views/remote_follow/new.html.haml @@ -6,7 +6,7 @@ .follow-prompt %h2= t('remote_follow.prompt') - = render partial: 'authorize_follows/card', locals: { account: @account } + = render partial: 'application/card', locals: { account: @account } = simple_form_for @remote_follow, as: :remote_follow, url: account_remote_follow_path(@account) do |f| = render 'shared/error_messages', object: @remote_follow diff --git a/app/views/remote_unfollows/success.html.haml b/app/views/remote_unfollows/success.html.haml index aa3c838a0..b007eedc7 100644 --- a/app/views/remote_unfollows/success.html.haml +++ b/app/views/remote_unfollows/success.html.haml @@ -5,6 +5,6 @@ .follow-prompt %h2= t('remote_unfollow.unfollowed') - = render 'card', account: @account + = render 'application/card', account: @account = render 'post_follow_actions' diff --git a/app/views/settings/migrations/show.html.haml b/app/views/settings/migrations/show.html.haml index b7c34761f..c69061d50 100644 --- a/app/views/settings/migrations/show.html.haml +++ b/app/views/settings/migrations/show.html.haml @@ -6,7 +6,7 @@ %p.hint= t('migrations.currently_redirecting') .fields-group - = render partial: 'authorize_follows/card', locals: { account: @migration.account } + = render partial: 'application/card', locals: { account: @migration.account } = render 'shared/error_messages', object: @migration diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index a84f8a7da..3940eb855 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -8,8 +8,7 @@ = f.input :display_name, placeholder: t('simple_form.labels.defaults.display_name'), hint: t('simple_form.hints.defaults.display_name', count: 30 - @account.display_name.size).html_safe = f.input :note, placeholder: t('simple_form.labels.defaults.note'), hint: t('simple_form.hints.defaults.note', count: 160 - @account.note.size).html_safe - .card.compact{ style: "background-image: url(#{@account.header.url(:original)})", data: { original_src: @account.header.url(:original) } } - .avatar= image_tag @account.avatar.url(:original), data: { original_src: @account.avatar.url(:original) } + = render 'application/card', account: @account .fields-group = f.input :avatar, wrapper: :with_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar') diff --git a/app/views/shared/_landing_strip.html.haml b/app/views/shared/_landing_strip.html.haml deleted file mode 100644 index 9a4144723..000000000 --- a/app/views/shared/_landing_strip.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.landing-strip - = image_tag asset_pack_path('logo.svg'), class: 'logo' - - %div - = t('landing_strip_html', name: content_tag(:span, display_name(account, custom_emojify: true), class: :emojify), link_to_root_path: link_to(content_tag(:strong, site_hostname), root_path)) - = t('landing_strip_signup_html', sign_up_path: open_registrations? ? new_user_registration_path : 'https://joinmastodon.org/#getting-started') diff --git a/app/views/stream_entries/_content_spoiler.html.haml b/app/views/stream_entries/_content_spoiler.html.haml deleted file mode 100644 index 798dfce67..000000000 --- a/app/views/stream_entries/_content_spoiler.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.media-spoiler-wrapper{ class: sensitive == false && 'media-spoiler-wrapper__visible' } - .spoiler-button - .icon-button.overlayed - %i.fa.fa-fw.fa-eye - .media-spoiler - %span= t('stream_entries.sensitive_content') - %span= t('stream_entries.click_to_show') diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index 85e90a237..aa160b979 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -1,16 +1,15 @@ -.detailed-status.light +.detailed-status.detailed-status--flex = link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do - %div - .avatar - = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo' + .detailed-status__display-avatar + = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'account__avatar u-photo' %span.display-name - %strong.p-name.emojify= display_name(status.account, custom_emojify: true) - %span= acct(status.account) + %bdi + %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true) + %span.display-name__account + = acct(status.account) + = fa_icon('lock') if status.account.locked? - - if !user_signed_in? || embedded_view? - = link_to account_remote_follow_path(status.account), class: 'button button-secondary logo-button', target: '_new' do - = render file: Rails.root.join('app', 'javascript', 'images', 'logo.svg') - = t('accounts.follow') + = account_action_button(status.account) .status__content.emojify< - if status.spoiler_text? @@ -30,6 +29,7 @@ .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } + = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: stream_link_target, rel: 'noopener' do %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) · @@ -40,20 +40,20 @@ = link_to status.application.name, status.application.website, class: 'detailed-status__application', target: '_blank', rel: 'noopener' · - if status.direct_visibility? - %span< + %span.detailed-status__link< = fa_icon('envelope') - elsif status.private_visibility? - %span< + %span.detailed-status__link< = fa_icon('lock') - else - %span< + %span.detailed-status__link< = fa_icon('retweet') - %span= status.reblogs_count + %span.detailed-status__reblogs= number_to_human status.reblogs_count, strip_insignificant_zeros: true · - %span< + %span.detailed-status__link< = fa_icon('star') - %span= status.favourites_count + %span.detailed-status__favorites= number_to_human status.favourites_count, strip_insignificant_zeros: true - if user_signed_in? · - = link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'open-in-web-link', target: '_blank' + = link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'detailed-status__application', target: '_blank' diff --git a/app/views/stream_entries/_media.html.haml b/app/views/stream_entries/_media.html.haml deleted file mode 100644 index 779f02c8d..000000000 --- a/app/views/stream_entries/_media.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.media-item - = link_to media.remote_url.blank? ? media.file.url(:original) : media.remote_url, style: media.image? ? "background-image: url(#{media.file.url(:original)})" : '', target: '_blank', rel: 'noopener', class: "u-#{media.video? || media.gifv? ? 'video' : 'photo'}" do - - unless media.image? - %video{ src: media.file.url(:original), autoplay: true, loop: true }/ diff --git a/app/views/stream_entries/_more.html.haml b/app/views/stream_entries/_more.html.haml deleted file mode 100644 index 9b1dfe4a7..000000000 --- a/app/views/stream_entries/_more.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -= link_to url, class: 'more light' do - = t('statuses.show_more') diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index 0f27585a1..676d367ca 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -1,18 +1,19 @@ -.status.light - .status__header - .status__meta - = link_to TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener' do - %time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) - %data.dt-published{ value: status.created_at.to_time.iso8601 } +.status + .status__info + = link_to TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener' do + %time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) + %data.dt-published{ value: status.created_at.to_time.iso8601 } = link_to TagManager.instance.url_for(status.account), class: 'status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do .status__avatar %div - = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo' + = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo account__avatar' %span.display-name - %strong.p-name.emojify= display_name(status.account, custom_emojify: true) - %span= acct(status.account) - + %bdi + %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true) + %span.display-name__account + = acct(status.account) + = fa_icon('lock') if status.account.locked? .status__content.emojify< - if status.spoiler_text? %p{ style: 'margin-bottom: 0' }< @@ -26,3 +27,16 @@ = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true - else = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } + + .status__action-bar + .status__action-bar-button.static-icon-button< + - if status.public_visibility? || status.unlisted_visibility? + = fa_icon 'retweet fw' + %span.detailed-status__reblogs= number_to_human status.reblogs_count, strip_insignificant_zeros: true + - elsif status.private_visibility? + = fa_icon 'lock fw' + - else + = fa_icon 'envelope fw' + .status__action-bar-button.static-icon-button< + = fa_icon 'star fw' + %span.detailed-status__favorites= number_to_human status.favourites_count, strip_insignificant_zeros: true diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index b87ca2177..320c9bc4f 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -16,24 +16,25 @@ - if status.reply? && include_threads - if @next_ancestor .entry{ class: entry_classes } - = render 'stream_entries/more', url: TagManager.instance.url_for(@next_ancestor) + = link_to_more TagManager.instance.url_for(@next_ancestor) = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id } .entry{ class: entry_classes } - if status.reblog? - .pre-header - .pre-header__icon - = fa_icon('retweet fw') + .status__prepend + .status__prepend-icon-wrapper + %i.status__prepend-icon.fa.fa-fw.fa-retweet %span = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do - %strong.emojify= display_name(status.account, custom_emojify: true) + %bdi + %strong.emojify= display_name(status.account, custom_emojify: true) = t('stream_entries.reblogged') - elsif pinned - .pre-header - .pre-header__icon - = fa_icon('thumb-tack fw') + .status__prepend + .status__prepend-icon-wrapper + %i.status__prepend-icon.fa.fa-fw.fa-thumb-tack %span = t('stream_entries.pinned') @@ -42,13 +43,13 @@ - if include_threads - if @since_descendant_thread_id .entry{ class: entry_classes } - = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) + = link_to_more short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) - @descendant_threads.each do |thread| = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id } - if thread[:next_status] .entry{ class: entry_classes } - = render 'stream_entries/more', url: TagManager.instance.url_for(thread[:next_status]) + = link_to_more TagManager.instance.url_for(thread[:next_status]) - if @next_descendant_thread .entry{ class: entry_classes } - = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) + = link_to_more short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) diff --git a/app/views/stream_entries/embed.html.haml b/app/views/stream_entries/embed.html.haml index b703c15d2..d20c1e93e 100644 --- a/app/views/stream_entries/embed.html.haml +++ b/app/views/stream_entries/embed.html.haml @@ -1,3 +1,3 @@ - cache @stream_entry.activity do - .activity-stream.activity-stream-headless + .activity-stream.activity-stream--headless = render "stream_entries/#{@type}", @type.to_sym => @stream_entry.activity, centered: true diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml index dfb83e747..9da6245dc 100644 --- a/app/views/stream_entries/show.html.haml +++ b/app/views/stream_entries/show.html.haml @@ -17,8 +17,9 @@ = render 'stream_entries/og_description', activity: @stream_entry.activity = render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account -- if show_landing_strip? - = render partial: 'shared/landing_strip', locals: { account: @stream_entry.account } - -.activity-stream.activity-stream-headless.h-entry - = render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true } +.grid + .column-0 + .activity-stream.activity-stream-headless.h-entry + = render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true } + .column-1 + = render 'application/sidebar' |