Merge branch 'develop' of https://code.gab.com/gab/social/gab-social into develop

This commit is contained in:
Rob Colbert 2019-08-20 19:29:52 -04:00
commit fc2c84bf21
75 changed files with 1001 additions and 317 deletions

View File

@ -4,6 +4,7 @@ class HomeController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :set_referrer_policy_header before_action :set_referrer_policy_header
before_action :set_initial_state_json before_action :set_initial_state_json
before_action :set_data_for_meta
def index def index
@body_classes = 'app-body' @body_classes = 'app-body'
@ -11,17 +12,40 @@ class HomeController < ApplicationController
private private
def set_data_for_meta
return if find_route_matches
if params[:username].present?
@account = Account.find_local(params[:username])
elsif params[:account_username].present?
@account = Account.find_local(params[:account_username])
if params[:id].present? && !@account.nil?
@status = @account.statuses.find(params[:id])
@stream_entry = @status.stream_entry
@type = @stream_entry.activity_type.downcase
end
end
if request.path.starts_with?('/tags') && params[:tag].present?
@tag = Tag.find_normalized(params[:tag])
end
end
def authenticate_user! def authenticate_user!
return if user_signed_in? return if user_signed_in?
# if no current user, dont allow to navigate to these paths # if no current user, dont allow to navigate to these paths
matches = request.path.match(/\A\/(home|groups|tags|lists|notifications|explore|follow_requests|blocks|domain_blocks|mutes)/) if find_route_matches
if matches
redirect_to(homepage_path) redirect_to(homepage_path)
end end
end end
def find_route_matches
request.path.match(/\A\/(home|groups|lists|notifications|explore|follow_requests|blocks|domain_blocks|mutes)/)
end
def set_initial_state_json def set_initial_state_json
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
@initial_state_json = serializable_resource.to_json @initial_state_json = serializable_resource.to_json

View File

@ -0,0 +1,14 @@
export const SIDEBAR_OPEN = 'SIDEBAR_OPEN';
export const SIDEBAR_CLOSE = 'SIDEBAR_CLOSE';
export function openSidebar() {
return {
type: SIDEBAR_OPEN,
};
};
export function closeSidebar() {
return {
type: SIDEBAR_CLOSE,
};
};

View File

@ -3,7 +3,7 @@ import openDB from '../storage/db';
import { evictStatus } from '../storage/modifier'; import { evictStatus } from '../storage/modifier';
import { deleteFromTimelines } from './timelines'; import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer'; import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
import { ensureComposeIsVisible } from './compose'; import { openModal } from './modal';
import { me } from 'gabsocial/initial_state'; import { me } from 'gabsocial/initial_state';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
@ -159,7 +159,7 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
if (withRedraft) { if (withRedraft) {
dispatch(redraft(status, response.data.text)); dispatch(redraft(status, response.data.text));
ensureComposeIsVisible(getState, routerHistory); dispatch(openModal('COMPOSE'));
} }
}).catch(error => { }).catch(error => {
dispatch(deleteStatusFail(id, error)); dispatch(deleteStatusFail(id, error));
@ -272,7 +272,7 @@ export function muteStatusFail(id, error) {
export function unmuteStatus(id) { export function unmuteStatus(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
if (!me) return; if (!me) return;
dispatch(unmuteStatusRequest(id)); dispatch(unmuteStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => { api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => {

View File

@ -0,0 +1,212 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link, NavLink } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import Avatar from './avatar';
import IconButton from './icon_button';
import Icon from './icon';
import DisplayName from './display_name';
import { closeSidebar } from '../actions/sidebar';
import { shortNumberFormat } from '../utils/numbers';
import { me } from '../initial_state';
import { makeGetAccount } from '../selectors';
const messages = defineMessages({
followers: { id: 'account.followers', defaultMessage: 'Followers' },
follows: { id: 'account.follows', defaultMessage: 'Follows' },
profile: { id: 'account.profile', defaultMessage: 'Profile' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
lists: { id: 'column.lists', defaultMessage: 'Lists', },
apps: { id: 'tabs_bar.apps', defaultMessage: 'Apps' },
news: { id: 'tabs_bar.news', defaultMessage: 'News' },
more: { id: 'sidebar.more', defaultMessage: 'More' },
partners: { id: 'promo.partners', defaultMessage: 'Affiliate Partners' },
store: { id: 'promo.store', defaultMessage: 'Store' },
})
const mapStateToProps = state => {
const getAccount = makeGetAccount();
return {
account: getAccount(state, me),
sidebarOpen: state.get('sidebar').sidebarOpen,
};
};
const mapDispatchToProps = (dispatch) => ({
onClose () {
dispatch(closeSidebar());
},
});
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class SidebarMenu extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
account: ImmutablePropTypes.map,
sidebarOpen: PropTypes.bool,
onClose: PropTypes.func.isRequired,
};
state = {
moreOpen: false,
}
componentDidUpdate () {
if (!me) return;
if (this.props.sidebarOpen) {
document.body.classList.add('with-modals--active');
} else {
document.body.classList.remove('with-modals--active');
}
}
toggleMore = () => {
this.setState({
moreOpen: !this.state.moreOpen
});
}
handleSidebarClose = () => {
this.props.onClose();
this.setState({
moreOpen: false,
});
}
render () {
const { sidebarOpen, intl, account } = this.props;
const { moreOpen } = this.state;
if (!me || !account) return null;
const acct = account.get('acct');
const classes = classNames('sidebar-menu__root', {
'sidebar-menu__root--visible': sidebarOpen,
});
const moreIcon = moreOpen ? 'minus' : 'plus';
const moreContainerStyle = { display: moreOpen ? 'block' : 'none' };
return (
<div className={classes}>
<div className='sidebar-menu__wrapper' role='button' onClick={this.handleSidebarClose} />
<div className='sidebar-menu'>
<div className='sidebar-menu-header'>
<span className='sidebar-menu-header__title'>Account Info</span>
<IconButton title='close' onClick={this.handleSidebarClose} icon='close' className='sidebar-menu-header__btn' />
</div>
<div className='sidebar-menu__content'>
<div className='sidebar-menu-profile'>
<div className='sidebar-menu-profile__avatar'>
<Link to={`/${acct}`} title={acct} onClick={this.handleSidebarClose}>
<Avatar account={account} />
</Link>
</div>
<div className='sidebar-menu-profile__name'>
<DisplayName account={account}/>
</div>
<div className='sidebar-menu-profile__stats'>
<NavLink className='sidebar-menu-profile-stat' to={`/${acct}/followers`} onClick={this.handleSidebarClose} title={intl.formatNumber(account.get('followers_count'))}>
<strong className='sidebar-menu-profile-stat__value'>{shortNumberFormat(account.get('followers_count'))}</strong>
<span className='sidebar-menu-profile-stat__label'>{intl.formatMessage(messages.followers)}</span>
</NavLink>
<NavLink className='sidebar-menu-profile-stat' to={`/${acct}/following`} onClick={this.handleSidebarClose} title={intl.formatNumber(account.get('following_count'))}>
<strong className='sidebar-menu-profile-stat__value'>{shortNumberFormat(account.get('following_count'))}</strong>
<span className='sidebar-menu-profile-stat__label'>{intl.formatMessage(messages.follows)}</span>
</NavLink>
</div>
</div>
<div className='sidebar-menu__section sidebar-menu__section--borderless'>
<NavLink className='sidebar-menu-item' to={`/${acct}`} onClick={this.handleSidebarClose}>
<Icon id='user' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.profile)}</span>
</NavLink>
<a className='sidebar-menu-item' href='https://blog.gab.com'>
<Icon id='align-left' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.news)}</span>
</a>
<a className='sidebar-menu-item' href='https://gumroad.com/getongab'>
<Icon id='dollar' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.store)}</span>
</a>
<a className='sidebar-menu-item' href='https://blog.gab.com/support-gab'>
<Icon id='users' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.partners)}</span>
</a>
<a className='sidebar-menu-item' href='https://apps.gab.com'>
<Icon id='th' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.apps)}</span>
</a>
<a className='sidebar-menu-item' href='/settings/preferences'>
<Icon id='cog' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.preferences)}</span>
</a>
</div>
<div className='sidebar-menu__section'>
<div className='sidebar-menu-item' onClick={this.toggleMore} role='button'>
<Icon id={moreIcon} fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.more)}</span>
</div>
<div style={moreContainerStyle}>
<NavLink className='sidebar-menu-item' to='/lists' onClick={this.handleSidebarClose}>
<Icon id='list' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.lists)}</span>
</NavLink>
<NavLink className='sidebar-menu-item' to='/follow_requests' onClick={this.handleSidebarClose}>
<Icon id='user-plus' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.follow_requests)}</span>
</NavLink>
<NavLink className='sidebar-menu-item' to='/blocks' onClick={this.handleSidebarClose}>
<Icon id='ban' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.blocks)}</span>
</NavLink>
<NavLink className='sidebar-menu-item' to='/domain_blocks' onClick={this.handleSidebarClose}>
<Icon id='sitemap' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.domain_blocks)}</span>
</NavLink>
<NavLink className='sidebar-menu-item' to='/mutes' onClick={this.handleSidebarClose}>
<Icon id='times-circle' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.mutes)}</span>
</NavLink>
<a className='sidebar-menu-item' href='/filters'>
<Icon id='filter' fixedWidth />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.filters)}</span>
</a>
</div>
</div>
<div className='sidebar-menu__section'>
<a className='sidebar-menu-item' href='/auth/sign_out' data-method='delete'>
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.logout)}</span>
</a>
</div>
</div>
</div>
</div>
);
}
}

View File

@ -45,7 +45,7 @@ export default class StatusContent extends React.PureComponent {
} }
link.classList.add('status-link'); link.classList.add('status-link');
let mention = this.props.status.get('mentions').find(item => link.href === `/${item.get('acct')}`); let mention = this.props.status.get('mentions').find(item => link.href === `${item.get('url')}`);
if (mention) { if (mention) {
link.addEventListener('click', this.onMentionClick.bind(this, mention), false); link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
@ -139,7 +139,7 @@ export default class StatusContent extends React.PureComponent {
const { status, reblogContent } = this.props; const { status, reblogContent } = this.props;
const properContent = status.get('contentHtml'); const properContent = status.get('contentHtml');
return reblogContent return reblogContent
? `${reblogContent} <div class='status__quote'>${properContent}</div>` ? `${reblogContent} <div class='status__quote'>${properContent}</div>`
: properContent; : properContent;

View File

@ -48,7 +48,7 @@ class ActionBar extends React.PureComponent {
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
menu.push({ text: intl.formatMessage(messages.filters), to: '/filters' }); menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
menu.push(null); menu.push(null);
menu.push({ text: intl.formatMessage(messages.keyboard_shortcuts), action: this.handleHotkeyClick }); menu.push({ text: intl.formatMessage(messages.keyboard_shortcuts), action: this.handleHotkeyClick });
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' }); menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });

View File

@ -1,32 +1,41 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { injectIntl, FormattedMessage } from 'react-intl';
import { me } from '../../../initial_state';
import { makeGetAccount } from '../../../selectors';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Icon from 'gabsocial/components/icon'; import Icon from 'gabsocial/components/icon';
const PromoPanel = () => ( export default class PromoPanel extends React.PureComponent {
<div className='promo-panel'> render() {
<div className='promo-panel__container'> return (
<div className='wtf-panel promo-panel'>
<div className='promo-panel__container'>
<div className='promo-panel-item'>
<a className='promo-panel-item__btn' href='https://blog.gab.com'>
<Icon id='align-left' className='promo-panel-item__icon' fixedWidth />
<FormattedMessage id='promo.gab_news' defaultMessage='Gab News' />
</a>
</div>
<div className='promo-panel-item'> <div className='promo-panel-item'>
<a className='promo-panel-item__btn button button-alternative-2' href='/invites'> <a className='promo-panel-item__btn' href='https://gumroad.com/getongab'>
<Icon id='envelope' className='promo-panel-item__icon' fixedWidth /> <Icon id='dollar' className='promo-panel-item__icon' fixedWidth />
<FormattedMessage id='promo.invite_heading' defaultMessage='Invite Friends' /> <FormattedMessage id='promo.store' defaultMessage='Store' />
</a> </a>
<p className='promo-panel-item__message promo-panel-item__message--dark'> </div>
<FormattedMessage
id='promo.invite_message' <div className='promo-panel-item'>
defaultMessage='Invite others to be a member of Gab.' <a className='promo-panel-item__btn' href='https://blog.gab.com/support-gab'>
/> <Icon id='users' className='promo-panel-item__icon' fixedWidth />
</p> <FormattedMessage id='promo.partners' defaultMessage='Affiliate Partners' />
</a>
</div>
<div className='promo-panel-item'>
<a className='promo-panel-item__btn' href='https://apps.gab.com'>
<Icon id='th' className='promo-panel-item__icon' fixedWidth />
<FormattedMessage id='promo.gab_apps' defaultMessage='Gab Apps' />
</a>
</div>
</div>
</div> </div>
)
</div> }
</div> }
);
export default PromoPanel;

View File

@ -2,47 +2,50 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { NavLink, withRouter } from 'react-router-dom'; import { NavLink, withRouter } from 'react-router-dom';
import { FormattedMessage, injectIntl } from 'react-intl'; import { FormattedMessage, injectIntl } from 'react-intl';
import { debounce } from 'lodash'; import { throttle } from 'lodash';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { isUserTouching } from '../../../is_mobile';
import { me } from '../../../initial_state'; import { me } from '../../../initial_state';
import { Link } from 'react-router-dom'; import classNames from 'classnames';
import NotificationsCounterIcon from './notifications_counter_icon'; import NotificationsCounterIcon from './notifications_counter_icon';
import SearchContainer from 'gabsocial/features/compose/containers/search_container'; import SearchContainer from 'gabsocial/features/compose/containers/search_container';
import Avatar from '../../../components/avatar'; import Avatar from '../../../components/avatar';
import ActionBar from 'gabsocial/features/compose/components/action_bar'; import ActionBar from 'gabsocial/features/compose/components/action_bar';
import { openModal } from '../../../actions/modal'; import { openModal } from '../../../actions/modal';
import { openSidebar } from '../../../actions/sidebar';
export const privateLinks = [ export const privateLinks = [
<NavLink className='tabs-bar__link--logo' to='/home#' data-preview-title-id='column.home' style={{ padding: '0' }}> <NavLink key='pr0' className='tabs-bar__link--logo' to='/home#' data-preview-title-id='column.home' style={{ padding: '0' }}>
<FormattedMessage id='tabs_bar.home' defaultMessage='Home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />
</NavLink>, </NavLink>,
<NavLink className='tabs-bar__link home' to='/home' data-preview-title-id='column.home' > <NavLink key='pr1' className='tabs-bar__link' to='/home' data-preview-title-id='column.home' >
<i className='tabs-bar__link__icon home'/>
<FormattedMessage id='tabs_bar.home' defaultMessage='Home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />
</NavLink>, </NavLink>,
<NavLink className='tabs-bar__link notifications' to='/notifications' data-preview-title-id='column.notifications' > <NavLink key='pr2' className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' >
<i className='tabs-bar__link__icon notifications'/>
<NotificationsCounterIcon /> <NotificationsCounterIcon />
<FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /> <FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' />
</NavLink>, </NavLink>,
<NavLink className='tabs-bar__link groups' to='/groups' data-preview-title-id='column.groups' > <NavLink key='pr3' className='tabs-bar__link' to='/groups' data-preview-title-id='column.groups' >
<i className='tabs-bar__link__icon groups'/>
<FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' /> <FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' />
</NavLink>, </NavLink>,
<a className='tabs-bar__link apps' href='https://apps.gab.com' data-preview-title-id='tabs_bar.apps' > <NavLink key='pr5' className='tabs-bar__link tabs-bar__link--search' to='/search' data-preview-title-id='tabs_bar.search' >
<FormattedMessage id='tabs_bar.apps' defaultMessage='Apps' /> <i className='tabs-bar__link__icon tabs-bar__link__icon--search'/>
</a>,
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' >
<FormattedMessage id='tabs_bar.search' defaultMessage='Search' /> <FormattedMessage id='tabs_bar.search' defaultMessage='Search' />
</NavLink>, </NavLink>,
]; ];
export const publicLinks = [ export const publicLinks = [
<a className='tabs-bar__link--logo' href='/#' data-preview-title-id='column.home' style={{ padding: '0' }}> <a key='pl0' className='tabs-bar__link--logo' href='/#' data-preview-title-id='column.home' style={{ padding: '0' }}>
<FormattedMessage id='tabs_bar.home' defaultMessage='Home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />
</a>, </a>,
<a className='tabs-bar__link home' href='/home' data-preview-title-id='column.home' > <a key='pl1' className='tabs-bar__link' href='/home' data-preview-title-id='column.home' >
<i className='tabs-bar__link__icon home'/>
<FormattedMessage id='tabs_bar.home' defaultMessage='Home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />
</a>, </a>,
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' > <NavLink key='pl2' className='tabs-bar__link tabs-bar__link--search' to='/search' data-preview-title-id='tabs_bar.search' >
<i className='tabs-bar__link__icon tabs-bar__link__icon--search'/>
<FormattedMessage id='tabs_bar.search' defaultMessage='Search' /> <FormattedMessage id='tabs_bar.search' defaultMessage='Search' />
</NavLink>, </NavLink>,
]; ];
@ -54,18 +57,104 @@ class TabsBar extends React.PureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
onOpenCompose: PropTypes.func, onOpenCompose: PropTypes.func,
onOpenSidebar: PropTypes.func.isRequired,
}
state = {
collapsed: false,
}
static contextTypes = {
router: PropTypes.object,
}
lastScrollTop = 0;
componentDidMount () {
this.window = window;
this.documentElement = document.scrollingElement || document.documentElement;
this.attachScrollListener();
// Handle initial scroll posiiton
this.handleScroll();
}
componentWillUnmount () {
this.detachScrollListener();
} }
setRef = ref => { setRef = ref => {
this.node = ref; this.node = ref;
} }
attachScrollListener () {
this.window.addEventListener('scroll', this.handleScroll);
}
detachScrollListener () {
this.window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = throttle(() => {
if (this.window) {
const { pageYOffset, innerWidth } = this.window;
if (innerWidth > 895) return;
const { scrollTop } = this.documentElement;
let st = pageYOffset || scrollTop;
if (st > this.lastScrollTop){
let offset = st - this.lastScrollTop;
if (offset > 50) this.setState({collapsed: true});
} else {
let offset = this.lastScrollTop - st;
if (offset > 50) this.setState({collapsed: false});
}
this.lastScrollTop = st <= 0 ? 0 : st;
}
}, 150, {
trailing: true,
});
render () { render () {
const { intl: { formatMessage }, account, onOpenCompose } = this.props; const { intl: { formatMessage }, account, onOpenCompose, onOpenSidebar } = this.props;
const { collapsed } = this.state;
const links = account ? privateLinks : publicLinks; const links = account ? privateLinks : publicLinks;
const pathname = this.context.router.route.location.pathname || '';
let pathTitle = '';
if (pathname.includes('/home')) {
pathTitle = 'Home';
} else if (pathname.includes('/timeline/all')) {
pathTitle = 'All';
} else if (pathname.includes('/group')) {
pathTitle = 'Groups';
} else if (pathname.includes('/tags')) {
pathTitle = 'Tags';
} else if (pathname.includes('/list')) {
pathTitle = 'Lists';
} else if (pathname.includes('/notifications')) {
pathTitle = 'Notifications';
} else if (pathname.includes('/search')) {
pathTitle = 'Search';
} else if (pathname.includes('/follow_requests')) {
pathTitle = 'Requests';
} else if (pathname.includes('/blocks')) {
pathTitle = 'Blocks';
} else if (pathname.includes('/domain_blocks')) {
pathTitle = 'Domain Blocks';
} else if (pathname.includes('/mutes')) {
pathTitle = 'Mutes';
} else {
pathTitle = 'Profile';
}
const classes = classNames('tabs-bar', {
'tabs-bar--collapsed': collapsed,
})
return ( return (
<nav className='tabs-bar' ref={this.setRef}> <nav className={classes} ref={this.setRef}>
<div className='tabs-bar__container'> <div className='tabs-bar__container'>
<div className='tabs-bar__split tabs-bar__split--left'> <div className='tabs-bar__split tabs-bar__split--left'>
{ {
@ -91,8 +180,10 @@ class TabsBar extends React.PureComponent {
<div className='flex'> <div className='flex'>
<div className='tabs-bar__profile'> <div className='tabs-bar__profile'>
<Avatar account={account} /> <Avatar account={account} />
<button className='tabs-bar__sidebar-btn' onClick={onOpenSidebar}></button>
<ActionBar account={account} size={34} /> <ActionBar account={account} size={34} />
</div> </div>
<span className='tabs-bar__page-name'>{pathTitle}</span>
<button className='tabs-bar__button-compose button' onClick={onOpenCompose} aria-label='Gab'> <button className='tabs-bar__button-compose button' onClick={onOpenCompose} aria-label='Gab'>
<span>Gab</span> <span>Gab</span>
</button> </button>
@ -126,6 +217,9 @@ const mapDispatchToProps = (dispatch) => ({
onOpenCompose() { onOpenCompose() {
dispatch(openModal('COMPOSE')); dispatch(openModal('COMPOSE'));
}, },
onOpenSidebar() {
dispatch(openSidebar());
},
}); });
export default injectIntl( export default injectIntl(

View File

@ -30,6 +30,7 @@ import GroupPage from 'gabsocial/pages/group_page';
import SearchPage from 'gabsocial/pages/search_page'; import SearchPage from 'gabsocial/pages/search_page';
import HomePage from 'gabsocial/pages/home_page'; import HomePage from 'gabsocial/pages/home_page';
import GroupSidebarPanel from '../groups/sidebar_panel'; import GroupSidebarPanel from '../groups/sidebar_panel';
import SidebarMenu from '../../components/sidebar_menu';
import { import {
Status, Status,
@ -193,7 +194,7 @@ class SwitchingColumnsArea extends React.PureComponent {
<WrappedRoute path='/groups/:id/edit' page={GroupPage} component={GroupEdit} content={children} /> <WrappedRoute path='/groups/:id/edit' page={GroupPage} component={GroupEdit} content={children} />
<WrappedRoute path='/groups/:id' page={GroupPage} component={GroupTimeline} content={children} /> <WrappedRoute path='/groups/:id' page={GroupPage} component={GroupTimeline} content={children} />
<WrappedRoute path='/tags/:id' component={HashtagTimeline} content={children} /> <WrappedRoute path='/tags/:id' publicRoute component={HashtagTimeline} content={children} />
<WrappedRoute path='/lists' layout={LAYOUT.DEFAULT} component={Lists} content={children} /> <WrappedRoute path='/lists' layout={LAYOUT.DEFAULT} component={Lists} content={children} />
<WrappedRoute path='/list/:id' page={HomePage} component={ListTimeline} content={children} /> <WrappedRoute path='/list/:id' page={HomePage} component={ListTimeline} content={children} />
@ -539,6 +540,7 @@ class UI extends React.PureComponent {
<LoadingBarContainer className='loading-bar' /> <LoadingBarContainer className='loading-bar' />
<ModalContainer /> <ModalContainer />
<UploadArea active={draggingOver} onClose={this.closeUploadModal} /> <UploadArea active={draggingOver} onClose={this.closeUploadModal} />
{ me && <SidebarMenu />}
</div> </div>
</HotKeys> </HotKeys>
); );

View File

@ -36,6 +36,7 @@ import groups from './groups';
import group_relationships from './group_relationships'; import group_relationships from './group_relationships';
import group_lists from './group_lists'; import group_lists from './group_lists';
import group_editor from './group_editor'; import group_editor from './group_editor';
import sidebar from './sidebar';
const reducers = { const reducers = {
dropdown_menu, dropdown_menu,
@ -75,6 +76,7 @@ const reducers = {
group_relationships, group_relationships,
group_lists, group_lists,
group_editor, group_editor,
sidebar,
}; };
export default combineReducers(reducers); export default combineReducers(reducers);

View File

@ -0,0 +1,12 @@
import { SIDEBAR_OPEN, SIDEBAR_CLOSE } from '../actions/sidebar';
export default function sidebar(state={}, action) {
switch(action.type) {
case SIDEBAR_OPEN:
return { sidebarOpen: true };
case SIDEBAR_CLOSE:
return { sidebarOpen: false };
default:
return state;
}
};

View File

@ -31,6 +31,7 @@
@import 'gabsocial/components/group-accounts'; @import 'gabsocial/components/group-accounts';
@import 'gabsocial/components/group-form'; @import 'gabsocial/components/group-form';
@import 'gabsocial/components/group-sidebar-panel'; @import 'gabsocial/components/group-sidebar-panel';
@import 'gabsocial/components/sidebar-menu';
@import 'gabsocial/polls'; @import 'gabsocial/polls';
@import 'gabsocial/introduction'; @import 'gabsocial/introduction';

File diff suppressed because one or more lines are too long

View File

@ -1575,7 +1575,7 @@ a.account__display-name {
.floating-action-button { .floating-action-button {
z-index: 1000; z-index: 1000;
display: none; display: none;
@media screen and (max-width: $nav-breakpoint-3) {display: flex;} @media screen and (max-width: 895px) {display: flex;}
position: fixed; position: fixed;
bottom: 14px; bottom: 14px;
right: 14px; right: 14px;
@ -1628,12 +1628,8 @@ a.account__display-name {
&__badge { &__badge {
position: absolute; position: absolute;
box-sizing: border-box; box-sizing: border-box;
left: -16px; left: -10px;
top: -14px; top: 1px;
@media screen and (max-width: $nav-breakpoint-1) {
left: 27px;
top: 0;
}
min-width: 16px; min-width: 16px;
height: 16px; height: 16px;
padding: 1px 3px 0; padding: 1px 3px 0;
@ -1653,32 +1649,38 @@ a.account__display-name {
.promo-panel { .promo-panel {
margin-top: 10px; margin-top: 10px;
padding: 10px 10px 20px 10px; }
border-bottom: 1px solid lighten($ui-base-color, 4%);
.promo-panel-item { .promo-panel-item {
display: block; display: block;
height: 42px;
line-height: 42px;
border-bottom: 1px solid lighten($ui-base-color, 8%);
&:not(:first-of-type) { &:last-of-type {
margin-top: 20px; border-bottom: none;
}
&__btn {
display: block;
text-align: left;
color: $primary-text-color;
text-decoration: none;
font-size: 15px;
padding: 0 20px;
&:hover {
color: darken($primary-text-color, 14%);
span {
text-decoration: underline;
}
}
} }
&__icon { &__icon {
margin-right: 12px; margin-right: 12px;
} }
&__message {
display: block;
font-size: 14px;
line-height: 16px;
margin-top: 6px;
color: $primary-text-color;
&--dark {
color: $ui-secondary-color;
}
}
}
} }
.drawer__pager { .drawer__pager {

View File

@ -0,0 +1,174 @@
.sidebar-menu {
display: flex;
position: fixed;
flex-direction: column;
width: 275px;
height: 100vh;
top: 0;
bottom: 0;
left: 0;
background: $gab-background-container;
transform: translateX(-275px);
transition: all 0.15s linear;
z-index: 10001;
body.theme-gabsocial-light & {
background: $gab-background-container-light;
}
&__root {
display: none;
}
&__wrapper {
display: block;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10000;
background-color: transparent;
transition: background-color 0.2s linear;
transition-delay: 0.1s;
}
&__content {
display: flex;
flex: 1 1;
flex-direction: column;
padding-bottom: 40px;
overflow: hidden;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
&__section {
display: block;
margin: 4px 0;
border-top: 1px solid lighten($ui-base-color, 4%);
&--borderless {
margin: 0;
border-top: none;
}
}
@media (max-width: 400px) {
width: 90vw;
}
}
.sidebar-menu__root--visible {
display: block;
.sidebar-menu {
transform: translateX(0);
}
.sidebar-menu__wrapper {
background-color: rgba(0,0,0,0.3);
}
}
.sidebar-menu-header {
display: flex;
height: 46px;
padding: 12px 14px;
border-bottom: 1px solid lighten($ui-base-color, 4%);
box-sizing: border-box;
align-items: center;
&__title {
display: block;
font-size: 18px;
font-weight: 600;
color: $primary-text-color;
}
&__btn {
margin-left: auto;
}
}
.sidebar-menu-profile {
display: block;
padding: 14px 18px;
&__avatar {
display: block;
width: 56px;
height: 56px;
}
&__name {
display: block;
margin-top: 10px;
.display-name__account {
display: block;
margin-top: 2px;
}
}
&__stats {
display: flex;
margin-top: 12px;
}
}
.sidebar-menu-profile-stat {
display: flex;
font-size: 14px;
text-decoration: none;
&:not(:first-of-type) {
margin-left: 18px;
}
&__value {
display: flex;
margin-right: 3px;
font-weight: 700;
color: $primary-text-color;
}
&__label {
display: flex;
color: $primary-text-color;
}
&:hover {
text-decoration: underline;
}
}
.sidebar-menu-item {
display: flex;
padding: 16px 18px;
cursor: pointer;
text-decoration: none;
color: $primary-text-color;
font-size: 15px;
font-weight: 400;
height: 50px;
box-sizing: border-box;
&:hover {
background-color: rgba($gab-brand-default, 0.1);
.fa {
color: $primary-text-color;
}
}
.fa {
margin-right: 10px;
}
&:hover {
&__title {
color: $primary-text-color;
}
}
}

View File

@ -8,6 +8,19 @@
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 1000; z-index: 1000;
transition: transform 0.2s ease;
// body.theme-gabsocial-light & {
// background: #fff;
// border-bottom: 1px solid #ddd;
// }
&--collapsed {
@media screen and (max-width: 895px) {
margin-top: -46px;
transform: translateY(-46px);
}
}
&__container { &__container {
display: flex; display: flex;
@ -19,7 +32,6 @@
// NOTE - might need to adjust this based on column sizing // NOTE - might need to adjust this based on column sizing
@media screen and (max-width: $nav-breakpoint-4) {padding: 0 10px;} @media screen and (max-width: $nav-breakpoint-4) {padding: 0 10px;}
} }
&__split { &__split {
@ -39,18 +51,10 @@
&__search-container { &__search-container {
display: block; display: block;
@media screen and (max-width: $nav-breakpoint-2) {display: none;}
width: 251px; width: 251px;
.search {
margin: 0;
}
@media (min-width:852px) and (max-width: 960px) { @media screen and (max-width: 1024px) {
width: 160px; display: none;
}
@media (min-width:$nav-breakpoint-2) and (max-width: 852px) {
width: 200px;
} }
} }
@ -77,8 +81,18 @@
height: 42px; height: 42px;
background-size: 42px 42px; background-size: 42px 42px;
} }
}
@media screen and (max-width: 895px) {
width: 30px;
height: 30px;
background-size: 30px 30px;
.account__avatar {
width: 30px;
height: 30px;
background-size: 30px 30px;
}
} }
.compose__action-bar { .compose__action-bar {
@ -92,8 +106,41 @@
i { i {
display: none; display: none;
} }
@media screen and (max-width: 895px) {
display: none;
}
} }
} }
&__sidebar-btn {
display: block;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
width: 30px;
opacity: 0;
@media (min-width: 895px) {
display: none;
}
}
&__page-name {
display: block;
margin-left: 18px;
line-height: 30px;
font-weight: 600;
font-size: 18px;
color: #fff;
@media (min-width: 895px) {
display: none;
}
}
&__button-compose { &__button-compose {
display: block; display: block;
@media screen and (max-width: $nav-breakpoint-3) {display: none;} @media screen and (max-width: $nav-breakpoint-3) {display: none;}
@ -122,81 +169,162 @@
.flex { .flex {
display: flex; display: flex;
} }
@media (max-width: 895px) {
height: 92px;
&__search-container,
&__button-compose {
display: none;
}
&__container {
max-width: 560px;
flex-direction: column-reverse;
padding: 0;
}
&__profile {
margin: 0;
}
&__split {
flex-direction: row;
align-items: stretch;
&--left {
margin: 0;
width: 100%;
}
&--right {
padding: 6px 0 6px 20px;
margin-left: 0;
margin-right: auto;
}
}
}
} }
.tabs-bar__link { .tabs-bar__link {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
margin: 0 25px 0 0; margin: 0 20px 0 0;
color: white; color: white;
text-decoration: none; text-decoration: none;
text-align: center;
background-repeat: no-repeat; @media screen and (max-width: 895px) {
background-image: url('../images/sprite-main-navigation-links.png');
background-size: auto 84px;
@media screen and (max-width: $nav-breakpoint-1) {
background-size: auto 120px;
width: 36px; width: 36px;
height: 42px; height: 42px;
margin: 4px 4px 0 0; margin: 4px 4px 0 0;
padding: 0 !important; justify-content: center;
&.active { &.active {
border-bottom: 4px solid $gab-default-text-light; border-bottom: 2px solid $gab-brand-default;
} }
& > span {display: none;} & > span {display: none;}
} }
// REMINDER - to add the remaining icons (globe / word balloon) from the sprite into this css as necessary > span {
&.home { font-size: 15px;
padding: 16px 0 0 26px; line-height: 50px;
background-position: 0 18px; margin-left: 4px;
&.active {background-position: 0 -52px;}
@media screen and (max-width: $nav-breakpoint-1) {
background-position: 6px 11px;
&.active {background-position: 6px -89px;}
}
} }
&.notifications {
padding: 16px 0 0 22px; &--search {
background-position: -140px 18px; @media (min-width: 1024px) {
&.active {background-position: -140px -52px;} display: none;
@media screen and (max-width: $nav-breakpoint-1) { }
background-position: -190px 11px; }
&.active {background-position: -190px -89px;}
} @media screen and (max-width:895px) {
} &.apps {
&.groups { display: none;
padding: 16px 0 0 29px; }
background-position: -280px 18px; }
&.active {background-position: -280px -52px;}
@media screen and (max-width: $nav-breakpoint-1) { &__icon {
background-position: -395px 11px; width: 20px;
&.active {background-position: -395px -89px;} background-repeat: no-repeat;
} background-image: url('../images/sprite-main-navigation-links.png');
} background-size: auto 84px;
&.apps {
padding: 16px 0 0 29px; @media screen and (max-width: 895px) {
background-position: -825px 18px; width: 32px;
@media screen and (max-width: $nav-breakpoint-1) { background-size: auto 120px;
background-position: -1170px 11px; }
}
} &.home {
&.optional { background-position: 0 18px;
display: none; @media screen and (max-width: 895px) {
@media screen and (max-width: $nav-breakpoint-2) { background-position: 1px 11px;
display: flex; }
background-position: -992px 11px; }
&.active {background-position: -992px -89px;} &.notifications {
background-position: -140px 18px;
@media screen and (max-width: 895px) {
background-position: -195px 11px;
}
}
&.groups {
background-position: -280px 18px;
@media screen and (max-width: 895px) {
background-position: -400px 11px;
}
}
&.apps {
background-position: -825px 18px;
}
&.tabs-bar__link__icon--search {
background-position: -697px 18px;
@media screen and (max-width: 895px) {
background-position: -995px 11px;
}
}
}
&.active {
color: $gab-text-highlight;
font-weight: 700;
}
&.active & {
&__icon {
&.home {
background-position: 0 -52px;
@media screen and (max-width: 895px) {
background-position: 1px -89px;
}
}
&.notifications {
background-position: -140px -52px;
@media screen and (max-width: 895px) {
background-position: -195px -89px;
}
}
&.groups {
background-position: -280px -52px;
@media screen and (max-width: 895px) {
background-position: -400px -89px;
}
}
&.tabs-bar__link__icon--search {
@media (min-width: 895px) and (max-width: 1024px) {
background-position: -697px -52px;
}
@media screen and (max-width: 895px) {
background-position: -995px -89px;
}
}
} }
} }
&.active {color: $gab-text-highlight;}
&--logo { &--logo {
display: block; display: block;
// NOTE - Revisit right-margin of home button / positioning between 376px and 350px // NOTE - Revisit right-margin of home button / positioning between 376px and 350px
// - want to keep the icons centered between logo and profile image while shrinking // - want to keep the icons centered between logo and profile image while shrinking
@media screen and (max-width: $nav-breakpoint-4) {display:none;} @media screen and (max-width: 895px) {display:none;}
width: 50px; width: 50px;
height: 50px; height: 50px;

View File

@ -15,7 +15,7 @@ class StatusFinder
case recognized_params[:controller] case recognized_params[:controller]
when 'stream_entries' when 'stream_entries'
StreamEntry.find(recognized_params[:id]).status StreamEntry.find(recognized_params[:id]).status
when 'statuses' when 'home'
Status.find(recognized_params[:id]) Status.find(recognized_params[:id])
else else
raise ActiveRecord::RecordNotFound raise ActiveRecord::RecordNotFound
@ -29,7 +29,7 @@ class StatusFinder
end end
def verify_action! def verify_action!
unless recognized_params[:action] == 'show' unless recognized_params[:action] == 'show' || recognized_params[:action] == 'index'
raise ActiveRecord::RecordNotFound raise ActiveRecord::RecordNotFound
end end
end end

View File

@ -0,0 +1,21 @@
- content_for :page_title do
= "#{display_name(account)} (@#{account.local_username_and_domain}) | #{site_hostname}"
- content_for :header_tags do
%meta{ name: 'description', content: account_description(account) }/
- if account.user&.setting_noindex
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'salmon', href: api_salmon_url(account.id) }/
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(account, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/rss+xml', href: account_url(account, format: 'rss') }/
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(account) }/
- if older_url
%link{ rel: 'next', href: older_url }/
- if newer_url
%link{ rel: 'prev', href: newer_url }/
= opengraph 'og:type', 'profile'
= render 'accounts/og', account: account, url: short_account_url(account, only_path: false)

View File

@ -1,25 +1,4 @@
- content_for :page_title do = render 'accounts/meta', account: @account, newer_url: @newer_url, older_url: @older_url
= "#{display_name(@account)} (@#{@account.local_username_and_domain})"
- content_for :header_tags do
%meta{ name: 'description', content: account_description(@account) }/
- if @account.user&.setting_noindex
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/rss+xml', href: account_url(@account, format: 'rss') }/
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@account) }/
- if @older_url
%link{ rel: 'next', href: @older_url }/
- if @newer_url
%link{ rel: 'prev', href: @newer_url }/
= opengraph 'og:type', 'profile'
= render 'og', account: @account, url: short_account_url(@account, only_path: false)
= render 'header', account: @account, with_bio: true = render 'header', account: @account, with_bio: true

View File

@ -133,7 +133,10 @@
%td %td
- if @account.is_pro? - if @account.is_pro?
=fa_icon 'check' =fa_icon 'check'
%time.formatted{ datetime: @account.pro_expires_at.iso8601, title: l(@account.pro_expires_at) }= l @account.pro_expires_at - if @account.pro_expires_at?
%time.formatted{ datetime: @account.pro_expires_at.iso8601, title: l(@account.pro_expires_at) }= l @account.pro_expires_at
- else
%td %td
- if @account.local? - if @account.local?
= table_link_to '', t('admin.accounts.edit_pro'), edit_pro_admin_account_path(@account.id), class: 'button' if can?(:verify, @account) = table_link_to '', t('admin.accounts.edit_pro'), edit_pro_admin_account_path(@account.id), class: 'button' if can?(:verify, @account)

View File

@ -18,6 +18,13 @@
%meta{ name: 'theme-color', content: '#282c37' }/ %meta{ name: 'theme-color', content: '#282c37' }/
%meta{ name: 'apple-mobile-web-app-capable', content: 'yes' }/ %meta{ name: 'apple-mobile-web-app-capable', content: 'yes' }/
- if @tag
= render 'tags/meta', tag: @tag, initial_state_json: @initial_state_json
- elsif @stream_entry && @account
= render 'stream_entries/meta', stream_entry: @stream_entry, account: @account
- elsif @account
= render 'accounts/meta', account: @account, older_url: nil, newer_url: nil
%title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title %title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title
= stylesheet_pack_tag 'common', media: 'all' = stylesheet_pack_tag 'common', media: 'all'

View File

@ -31,10 +31,6 @@
.fields-group .fields-group
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot') = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
- if Setting.profile_directory
.fields-group
= f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable_html', min_followers: Account::MIN_FOLLOWERS_DISCOVERY, path: explore_path)
%hr.spacer/ %hr.spacer/
.fields-row .fields-row

View File

@ -75,4 +75,4 @@
- if user_signed_in? - if user_signed_in?
· ·
= link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'detailed-status__application', target: '_blank' = link_to t('statuses.open_in_web'), web_url("#{status.account.acct}/posts/#{status.id}"), class: 'detailed-status__application', target: '_blank'

View File

@ -0,0 +1,18 @@
- content_for :page_title do
= t('statuses.title', name: display_name(account), quote: truncate(stream_entry.activity.spoiler_text.presence || stream_entry.activity.text, length: 50, omission: '…', escape: false)) + " | #{site_hostname}"
- content_for :header_tags do
- if account.user&.setting_noindex
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(account, stream_entry, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(account, stream_entry), format: 'json') }/
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(stream_entry.activity) }/
= opengraph 'og:site_name', site_title
= opengraph 'og:type', 'article'
= opengraph 'og:title', "#{display_name(account)} (@#{account.local_username_and_domain})"
= opengraph 'og:url', short_account_status_url(account, stream_entry.activity)
= render 'stream_entries/og_description', activity: stream_entry.activity
= render 'stream_entries/og_image', activity: stream_entry.activity, account: account

View File

@ -1,21 +1,4 @@
- content_for :page_title do = render 'stream_entries/meta', stream_entry: @stream_entry, account: @account
= t('statuses.title', name: display_name(@account), quote: truncate(@stream_entry.activity.spoiler_text.presence || @stream_entry.activity.text, length: 50, omission: '…', escape: false))
- content_for :header_tags do
- if @account.user&.setting_noindex
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@stream_entry.activity) }/
= opengraph 'og:site_name', site_title
= opengraph 'og:type', 'article'
= opengraph 'og:title', "#{display_name(@account)} (@#{@account.local_username_and_domain})"
= opengraph 'og:url', short_account_status_url(@account, @stream_entry.activity)
= render 'stream_entries/og_description', activity: @stream_entry.activity
= render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account
.grid .grid
.column-0 .column-0

View File

@ -0,0 +1,10 @@
- content_for :page_title do
= "##{@tag.name} - Hashtag | #{site_hostname}"
- content_for :header_tags do
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
= javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
= render 'tags/og'

View File

@ -1,13 +1,4 @@
- content_for :page_title do = render 'tags/meta', tag: @tag, initial_state_json: @initial_state_json
= "##{@tag.name}"
- content_for :header_tags do
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
= javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
= render 'og'
.page-header .page-header
%h1= "##{@tag.name}" %h1= "##{@tag.name}"

View File

@ -689,12 +689,12 @@ ar:
two: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون! two: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
zero: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون! zero: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
subject: subject:
few: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418" few: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى"
many: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418" many: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى"
one: "إشعار واحد 1 منذ آخر زيارة لك لـ \U0001F418" one: "إشعار واحد 1 منذ آخر زيارة لك لـ"
other: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418" other: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى"
two: "إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418" two: "إشعارات جديدة منذ آخر زيارة لك إلى"
zero: "إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418" zero: "إشعارات جديدة منذ آخر زيارة لك إلى"
title: أثناء فترة غيابك … title: أثناء فترة غيابك …
favourite: favourite:
body: 'أُعجب %{name} بمنشورك :' body: 'أُعجب %{name} بمنشورك :'

View File

@ -224,7 +224,7 @@ ast:
body: Equí hai un resume de los mensaxes que nun viesti dende la última visita'l %{since} body: Equí hai un resume de los mensaxes que nun viesti dende la última visita'l %{since}
mention: "%{name} mentóte en:" mention: "%{name} mentóte en:"
subject: subject:
other: "%{count} avisos nuevos dende la última visita \U0001F418" other: "%{count} avisos nuevos dende la última visita"
follow: follow:
body: "¡Agora %{name} ta siguiéndote!" body: "¡Agora %{name} ta siguiéndote!"
title: Siguidor nuevu title: Siguidor nuevu

View File

@ -79,8 +79,8 @@ bg:
one: Имаш един нов последовател! Ура! one: Имаш един нов последовател! Ура!
other: Имаш %{count} нови последователи! Изумително! other: Имаш %{count} нови последователи! Изумително!
subject: subject:
one: "1 ново известие от последното ти посещение \U0001F418" one: "1 ново известие от последното ти посещение"
other: "%{count} нови известия от последното ти посещение \U0001F418" other: "%{count} нови известия от последното ти посещение"
favourite: favourite:
body: 'Публикацията ти беше харесана от %{name}:' body: 'Публикацията ти беше харесана от %{name}:'
subject: "%{name} хареса твоята публикация" subject: "%{name} хареса твоята публикация"

View File

@ -723,8 +723,8 @@ ca:
one: A més, has adquirit un nou seguidor durant la teva absència! Visca! one: A més, has adquirit un nou seguidor durant la teva absència! Visca!
other: A més, has adquirit %{count} nous seguidors mentre estaves fora! Increïble! other: A més, has adquirit %{count} nous seguidors mentre estaves fora! Increïble!
subject: subject:
one: "1 notificació nova des de la darrera visita \U0001F418" one: "1 notificació nova des de la darrera visita"
other: "%{count} notificacions noves des de la darrera visita \U0001F418" other: "%{count} notificacions noves des de la darrera visita"
title: Durant la teva absència… title: Durant la teva absència…
favourite: favourite:
body: "%{name} ha marcat com a favorit el teu estat:" body: "%{name} ha marcat com a favorit el teu estat:"

View File

@ -723,8 +723,8 @@ co:
one: Avete ancu unabbunatu novu! one: Avete ancu unabbunatu novu!
other: Avete ancu %{count} abbunati novi! other: Avete ancu %{count} abbunati novi!
subject: subject:
one: "Una nutificazione nova dapoi à a vostrultima visita \U0001F418" one: "Una nutificazione nova dapoi à a vostrultima visita"
other: "%{count} nutificazione nove dapoi à a vostrultima visita \U0001F418" other: "%{count} nutificazione nove dapoi à a vostrultima visita"
title: Dapoi lultima volta… title: Dapoi lultima volta…
favourite: favourite:
body: "%{name} hà aghjuntu u vostru statutu à i so favuriti :" body: "%{name} hà aghjuntu u vostru statutu à i so favuriti :"

View File

@ -733,9 +733,9 @@ cs:
one: Navíc jste získal/a jednoho nového sledujícího, zatímco jste byl/a pryč! Hurá! one: Navíc jste získal/a jednoho nového sledujícího, zatímco jste byl/a pryč! Hurá!
other: Navíc jste získal/a %{count} nových sledujících, zatímco jste byl/a pryč! Úžasné! other: Navíc jste získal/a %{count} nových sledujících, zatímco jste byl/a pryč! Úžasné!
subject: subject:
few: "%{count} nová oznámení od vaší poslední návštěvy \U0001F418" few: "%{count} nová oznámení od vaší poslední návštěvy"
one: "1 nové oznámení od vaší poslední návštěvy \U0001F418" one: "1 nové oznámení od vaší poslední návštěvy"
other: "%{count} nových oznámení od vaší poslední návštěvy \U0001F418" other: "%{count} nových oznámení od vaší poslední návštěvy"
title: Ve vaší nepřítomnosti… title: Ve vaší nepřítomnosti…
favourite: favourite:
body: 'Váš toot si oblíbil/a %{name}:' body: 'Váš toot si oblíbil/a %{name}:'

View File

@ -591,8 +591,8 @@ da:
one: Du har også fået dig en ny følger mens du var væk! Sådan! one: Du har også fået dig en ny følger mens du var væk! Sådan!
other: Du har også fået %{count} nye følgere mens du var væk! Fantastisk! other: Du har også fået %{count} nye følgere mens du var væk! Fantastisk!
subject: subject:
one: "1 ny notifikation siden du sidst var her \U0001F418" one: "1 ny notifikation siden du sidst var her"
other: "%{count} nye notifikationer siden du sidst var her \U0001F418" other: "%{count} nye notifikationer siden du sidst var her"
title: Mens du var væk... title: Mens du var væk...
favourite: favourite:
body: 'Din status blev favoriseret af %{name}:' body: 'Din status blev favoriseret af %{name}:'

View File

@ -723,8 +723,8 @@ de:
one: Außerdem ist dir seit du weg warst ein weiteres Wesen gefolgt! Juhu! one: Außerdem ist dir seit du weg warst ein weiteres Wesen gefolgt! Juhu!
other: Außerdem sind dir seit du weg warst %{count} weitere Wesen gefolgt! Großartig! other: Außerdem sind dir seit du weg warst %{count} weitere Wesen gefolgt! Großartig!
subject: subject:
one: "1 neue Mitteilung seit deinem letzten Besuch \U0001F418" one: "1 neue Mitteilung seit deinem letzten Besuch"
other: "%{count} neue Mitteilungen seit deinem letzten Besuch \U0001F418" other: "%{count} neue Mitteilungen seit deinem letzten Besuch"
title: In deiner Abwesenheit... title: In deiner Abwesenheit...
favourite: favourite:
body: 'Dein Beitrag wurde von %{name} favorisiert:' body: 'Dein Beitrag wurde von %{name} favorisiert:'

View File

@ -712,8 +712,8 @@ el:
one: Επίσης, απέκτησες έναν νέο ακόλουθο ενώ ήσουν μακριά! one: Επίσης, απέκτησες έναν νέο ακόλουθο ενώ ήσουν μακριά!
other: Επίσης, απέκτησες %{count} νέους ακόλουθους ενώ ήσουν μακριά! Εκπληκτικό! other: Επίσης, απέκτησες %{count} νέους ακόλουθους ενώ ήσουν μακριά! Εκπληκτικό!
subject: subject:
one: "1 νέα ειδοποίηση από την τελευταία επίσκεψή σου \U0001F418" one: "1 νέα ειδοποίηση από την τελευταία επίσκεψή σου"
other: "%{count} νέες ειδοποιήσεις από την τελευταία επίσκεψή σου \U0001F418" other: "%{count} νέες ειδοποιήσεις από την τελευταία επίσκεψή σου"
title: Ενώ έλειπες... title: Ενώ έλειπες...
favourite: favourite:
body: 'Η κατάστασή σου αγαπήθηκε από τον/την %{name}:' body: 'Η κατάστασή σου αγαπήθηκε από τον/την %{name}:'

View File

@ -738,8 +738,8 @@ en:
one: Also, you have acquired one new follower while being away! Yay! one: Also, you have acquired one new follower while being away! Yay!
other: Also, you have acquired %{count} new followers while being away! Amazing! other: Also, you have acquired %{count} new followers while being away! Amazing!
subject: subject:
one: "1 new notification since your last visit \U0001F418" one: "1 new notification since your last visit"
other: "%{count} new notifications since your last visit \U0001F418" other: "%{count} new notifications since your last visit"
title: In your absence... title: In your absence...
favourite: favourite:
body: 'Your status was favorited by %{name}:' body: 'Your status was favorited by %{name}:'
@ -1109,7 +1109,7 @@ en:
image_hint: "JPG/PNG/GIF up to 4MB" image_hint: "JPG/PNG/GIF up to 4MB"
already_requested: "You have a verification request awaiting moderation." already_requested: "You have a verification request awaiting moderation."
already_verified_html: <p>On Gab, a verification badge shows that a person is who they say they are. You are already a verified account.</p> already_verified_html: <p>On Gab, a verification badge shows that a person is who they say they are. You are already a verified account.</p>
only_allowed_for_pro: "<em>Only available to Gab PRO users.</em>" only_allowed_for_pro: Only available to Gab PRO users.
explanation_html: <p>On Gab, a verification badge shows that a person is who they say they are. PRO users can upload an image to start the verification process immediately. It will take 5-7 business days for us to review.</p><p>All you need to do is upload an image of yourself holding a government-issued ID card with the name section visible. No other information on the ID is needed. Once verified, this information is immediately deleted.</p><p>The name on your ID must match the display name on your profile and must include your first and last name. Middle names or initials are optional.</p><p>Please make sure that the image is high-resolution enough to read the name on your ID card and your face is visible.</p><br> explanation_html: <p>On Gab, a verification badge shows that a person is who they say they are. PRO users can upload an image to start the verification process immediately. It will take 5-7 business days for us to review.</p><p>All you need to do is upload an image of yourself holding a government-issued ID card with the name section visible. No other information on the ID is needed. Once verified, this information is immediately deleted.</p><p>The name on your ID must match the display name on your profile and must include your first and last name. Middle names or initials are optional.</p><p>Please make sure that the image is high-resolution enough to read the name on your ID card and your face is visible.</p><br>
moderation: moderation:
title: "Verification Request Moderation" title: "Verification Request Moderation"

View File

@ -707,8 +707,8 @@ en_GB:
one: Also, you have acquired one new follower while being away! Yay! one: Also, you have acquired one new follower while being away! Yay!
other: Also, you have acquired %{count} new followers while being away! Amazing! other: Also, you have acquired %{count} new followers while being away! Amazing!
subject: subject:
one: "1 new notification since your last visit \U0001F418" one: "1 new notification since your last visit"
other: "%{count} new notifications since your last visit \U0001F418" other: "%{count} new notifications since your last visit"
title: In your absence... title: In your absence...
favourite: favourite:
body: 'Your status was favourited by %{name}:' body: 'Your status was favourited by %{name}:'

View File

@ -719,8 +719,8 @@ eo:
one: Ankaŭ, vi ekhavis novan sekvanton en via foresto! Jej! one: Ankaŭ, vi ekhavis novan sekvanton en via foresto! Jej!
other: Ankaŭ, vi ekhavis %{count} novajn sekvantojn en via foresto! Mirinde! other: Ankaŭ, vi ekhavis %{count} novajn sekvantojn en via foresto! Mirinde!
subject: subject:
one: "1 nova sciigo ekde via lasta vizito \U0001F418" one: "1 nova sciigo ekde via lasta vizito"
other: "%{count} novaj sciigoj ekde via lasta vizito \U0001F418" other: "%{count} novaj sciigoj ekde via lasta vizito"
title: En via foresto… title: En via foresto…
favourite: favourite:
body: "%{name} stelumis vian mesaĝon:" body: "%{name} stelumis vian mesaĝon:"

View File

@ -596,8 +596,8 @@ es:
one: "¡Ademas, has adquirido un nuevo seguidor mientras no estabas! ¡Hurra!" one: "¡Ademas, has adquirido un nuevo seguidor mientras no estabas! ¡Hurra!"
other: "¡Ademas, has adquirido %{count} nuevos seguidores mientras no estabas! ¡Genial!" other: "¡Ademas, has adquirido %{count} nuevos seguidores mientras no estabas! ¡Genial!"
subject: subject:
one: "1 nueva notificación desde tu última visita \U0001F418" one: "1 nueva notificación desde tu última visita"
other: "%{count} nuevas notificaciones desde tu última visita \U0001F418" other: "%{count} nuevas notificaciones desde tu última visita"
title: En tu ausencia… title: En tu ausencia…
favourite: favourite:
body: 'Tu estado fue marcado como favorito por %{name}:' body: 'Tu estado fue marcado como favorito por %{name}:'

View File

@ -662,8 +662,8 @@ eu:
one: Kanpoan zeundela jarraitzaile berri bat gehitu zaizu! one: Kanpoan zeundela jarraitzaile berri bat gehitu zaizu!
other: Kanpoan zeundela %{count} jarraitzaile berri bat gehitu zaizkizu! other: Kanpoan zeundela %{count} jarraitzaile berri bat gehitu zaizkizu!
subject: subject:
one: "Jakinarazpen berri bat azken bisitatik \U0001F418" one: "Jakinarazpen berri bat azken bisitatik"
other: "%{count} jakinarazpen berri azken bisitatik \U0001F418" other: "%{count} jakinarazpen berri azken bisitatik"
title: Kanpoan zeundela... title: Kanpoan zeundela...
favourite: favourite:
body: "%{name}(e)k zure mezua gogoko du:" body: "%{name}(e)k zure mezua gogoko du:"

View File

@ -712,8 +712,8 @@ fa:
one: در ضمن، وقتی که نبودید یک پیگیر تازه پیدا کردید! ای ول! one: در ضمن، وقتی که نبودید یک پیگیر تازه پیدا کردید! ای ول!
other: در ضمن، وقتی که نبودید %{count} پیگیر تازه پیدا کردید! چه عالی! other: در ضمن، وقتی که نبودید %{count} پیگیر تازه پیدا کردید! چه عالی!
subject: subject:
one: "یک اعلان تازه از زمان آخرین بازدید شما \U0001F418" one: "یک اعلان تازه از زمان آخرین بازدید شما"
other: "%{count} اعلان تازه از زمان آخرین بازدید شما \U0001F418" other: "%{count} اعلان تازه از زمان آخرین بازدید شما"
title: در مدتی که نبودید... title: در مدتی که نبودید...
favourite: favourite:
body: "%{name} این نوشتهٔ شما را پسندید:" body: "%{name} این نوشتهٔ شما را پسندید:"

View File

@ -507,8 +507,8 @@ fi:
one: Olet myös saanut yhden uuden seuraajan! Juhuu! one: Olet myös saanut yhden uuden seuraajan! Juhuu!
other: Olet myös saanut %{count} uutta seuraajaa! Aivan mahtavaa! other: Olet myös saanut %{count} uutta seuraajaa! Aivan mahtavaa!
subject: subject:
one: "1 uusi ilmoitus viime käyntisi jälkeen \U0001F418" one: "1 uusi ilmoitus viime käyntisi jälkeen"
other: "%{count} uutta ilmoitusta viime käyntisi jälkeen \U0001F418" other: "%{count} uutta ilmoitusta viime käyntisi jälkeen"
title: Poissaollessasi… title: Poissaollessasi…
favourite: favourite:
body: "%{name} tykkäsi tilastasi:" body: "%{name} tykkäsi tilastasi:"

View File

@ -663,8 +663,8 @@ fr:
one: Vous avez un⋅e nouvel⋅le abonné⋅e! Youpi! one: Vous avez un⋅e nouvel⋅le abonné⋅e! Youpi!
other: Vous avez %{count} nouveaux⋅elles abonné⋅e·s! Incroyable! other: Vous avez %{count} nouveaux⋅elles abonné⋅e·s! Incroyable!
subject: subject:
one: "Une nouvelle notification depuis votre dernière visite \U0001F418" one: "Une nouvelle notification depuis votre dernière visite"
other: "%{count} nouvelles notifications depuis votre dernière visite \U0001F418" other: "%{count} nouvelles notifications depuis votre dernière visite"
title: Pendant votre absence… title: Pendant votre absence…
favourite: favourite:
body: "%{name} a ajouté votre pouet à ses favoris:" body: "%{name} a ajouté votre pouet à ses favoris:"

View File

@ -707,8 +707,8 @@ gl:
one: Ademáis, ten unha nova seguidora desde entón! Ben! one: Ademáis, ten unha nova seguidora desde entón! Ben!
other: Ademáis, obtivo %{count} novas seguidoras desde entón! Tremendo! other: Ademáis, obtivo %{count} novas seguidoras desde entón! Tremendo!
subject: subject:
one: "1 nova notificación desde a súa última visita \U0001F418" one: "1 nova notificación desde a súa última visita"
other: "%{count} novas notificacións desde a súa última visita \U0001F418" other: "%{count} novas notificacións desde a súa última visita"
title: Na súa ausencia... title: Na súa ausencia...
favourite: favourite:
body: 'O seu estado foi marcado favorito por %{name}:' body: 'O seu estado foi marcado favorito por %{name}:'

View File

@ -266,8 +266,8 @@ he:
one: נוסף לך עוקב! סחתיין! one: נוסף לך עוקב! סחתיין!
other: נוספו לך %{count} עוקבים חדשים! מעולה! other: נוספו לך %{count} עוקבים חדשים! מעולה!
subject: subject:
one: "התראה חדשה אחת מאז ביקורך האחרון \U0001F418" one: "התראה חדשה אחת מאז ביקורך האחרון"
other: "%{count} התראות חדשות \U0001F418" other: "%{count} התראות חדשות"
favourite: favourite:
body: 'חצרוצך חובב על ידי %{name}:' body: 'חצרוצך חובב על ידי %{name}:'
subject: חצרוצך חובב על ידי %{name} subject: חצרוצך חובב על ידי %{name}

View File

@ -73,7 +73,7 @@ hr:
body: Ovo je kratak sažetak propuštenog od tvog prošlog posjeta %{since} body: Ovo je kratak sažetak propuštenog od tvog prošlog posjeta %{since}
mention: "%{name} te je spomenuo:" mention: "%{name} te je spomenuo:"
new_followers_summary: Imaš %{count} novih sljedbenika! Prekrašno! new_followers_summary: Imaš %{count} novih sljedbenika! Prekrašno!
subject: "%{count} novih notifikacija od tvog prošlog posjeta \U0001F418" subject: "%{count} novih notifikacija od tvog prošlog posjeta"
favourite: favourite:
body: 'Tvoj status je %{name} označio kao omiljen:' body: 'Tvoj status je %{name} označio kao omiljen:'
subject: "%{name} je označio kao omiljen tvoj status" subject: "%{name} je označio kao omiljen tvoj status"

View File

@ -431,8 +431,8 @@ hu:
one: Sőt, egy új követőd is lett, amióta nem jártál itt. Hurrá! one: Sőt, egy új követőd is lett, amióta nem jártál itt. Hurrá!
other: Sőt, %{count} új követőd is lett, amióta nem jártál itt. Hihetetlen! other: Sőt, %{count} új követőd is lett, amióta nem jártál itt. Hihetetlen!
subject: subject:
one: "Egy új értesítésed érkezett legutóbbi látogatásod óta \U0001F418" one: "Egy új értesítésed érkezett legutóbbi látogatásod óta"
other: "%{count} új értesítésed érkezett legutóbbi látogatásod óta \U0001F418" other: "%{count} új értesítésed érkezett legutóbbi látogatásod óta"
title: Amíg távol voltál… title: Amíg távol voltál…
favourite: favourite:
body: 'Az állapotodat kedvencnek jelölte %{name}:' body: 'Az állapotodat kedvencnek jelölte %{name}:'

View File

@ -290,8 +290,8 @@ id:
one: Anda mendapatkan satu pengikut baru! Hore! one: Anda mendapatkan satu pengikut baru! Hore!
other: Anda mendapatkan %{count} pengikut baru! Luar biasa! other: Anda mendapatkan %{count} pengikut baru! Luar biasa!
subject: subject:
one: "1 notifikasi baru sejak kunjungan terakhir anda pada \U0001F418" one: "1 notifikasi baru sejak kunjungan terakhir anda pada"
other: "%{count} notifikasi baru sejak kunjungan terakhir anda pada \U0001F418" other: "%{count} notifikasi baru sejak kunjungan terakhir anda pada"
favourite: favourite:
body: 'Status anda disukai oleh %{name}:' body: 'Status anda disukai oleh %{name}:'
subject: "%{name} menyukai status anda" subject: "%{name} menyukai status anda"

View File

@ -191,8 +191,8 @@ io:
one: Tu obtenis nova sequanto! Yey! one: Tu obtenis nova sequanto! Yey!
other: Tu obtenis %{count} nova sequanti! Astonive! other: Tu obtenis %{count} nova sequanti! Astonive!
subject: subject:
one: "1 nova savigo depos tua lasta vizito \U0001F418" one: "1 nova savigo depos tua lasta vizito"
other: "%{count} nova savigi depos tua lasta vizito \U0001F418" other: "%{count} nova savigi depos tua lasta vizito"
favourite: favourite:
body: "%{name} favoris tua mesajo:" body: "%{name} favoris tua mesajo:"
subject: "%{name} favoris tua mesajo" subject: "%{name} favoris tua mesajo"

View File

@ -640,8 +640,8 @@ it:
one: E inoltre hai ricevuto un nuovo seguace mentre eri assente! Urrà! one: E inoltre hai ricevuto un nuovo seguace mentre eri assente! Urrà!
other: Inoltre, hai acquisito %{count} nuovi seguaci mentre eri assente! Incredibile! other: Inoltre, hai acquisito %{count} nuovi seguaci mentre eri assente! Incredibile!
subject: subject:
one: "1 nuova notifica dalla tua ultima visita \U0001F418" one: "1 nuova notifica dalla tua ultima visita"
other: "%{count} nuove notifiche dalla tua ultima visita \U0001F418" other: "%{count} nuove notifiche dalla tua ultima visita"
title: In tua assenza… title: In tua assenza…
favourite: favourite:
body: 'Il tuo status è stato apprezzato da %{name}:' body: 'Il tuo status è stato apprezzato da %{name}:'

View File

@ -723,8 +723,8 @@ ja:
one: また、離れている間に新たなフォロワーを獲得しました! one: また、離れている間に新たなフォロワーを獲得しました!
other: また、離れている間に%{count} 人の新たなフォロワーを獲得しました! other: また、離れている間に%{count} 人の新たなフォロワーを獲得しました!
subject: subject:
one: "新しい1件の通知 \U0001F418" one: "新しい1件の通知"
other: "新しい%{count}件の通知 \U0001F418" other: "新しい%{count}件の通知"
title: 不在の間に… title: 不在の間に…
favourite: favourite:
body: "%{name} さんにお気に入り登録された、あなたのトゥートがあります:" body: "%{name} さんにお気に入り登録された、あなたのトゥートがあります:"

View File

@ -559,8 +559,8 @@ ka:
one: ასევე, არყოფნისას შეგეძინათ ერთი ახალი მიმდევარი! იეი! one: ასევე, არყოფნისას შეგეძინათ ერთი ახალი მიმდევარი! იეი!
other: ასევე, არყოფნისას შეგეძინათ %{count} ახალი მიმდევარი! შესანიშნავია! other: ასევე, არყოფნისას შეგეძინათ %{count} ახალი მიმდევარი! შესანიშნავია!
subject: subject:
one: "1 ახალი შეტყობინება თქვენი ბოლო სტუმრობის შემდეგ \U0001F418" one: "1 ახალი შეტყობინება თქვენი ბოლო სტუმრობის შემდეგ"
other: "%{count} ახალი შეტყობინება თქვენი ბოლო სტუმრობის შემდეგ \U0001F418" other: "%{count} ახალი შეტყობინება თქვენი ბოლო სტუმრობის შემდეგ"
title: თქვენს არყოფნაში... title: თქვენს არყოფნაში...
favourite: favourite:
body: 'თქვენი სტატუსი ფავორიტი გახადა %{name}-მა:' body: 'თქვენი სტატუსი ფავორიტი გახადა %{name}-მა:'

View File

@ -663,8 +663,8 @@ kk:
one: Сондай-ақ, сіз бір жаңа оқырман таптыңыз! Алақай! one: Сондай-ақ, сіз бір жаңа оқырман таптыңыз! Алақай!
other: Сондай-ақ, сіз %{count} жаңа оқырман таптыңыз! Керемет! other: Сондай-ақ, сіз %{count} жаңа оқырман таптыңыз! Керемет!
subject: subject:
one: "Соңғы кіруіңізден кейін 1 ескертпе келіпті \U0001F418" one: "Соңғы кіруіңізден кейін 1 ескертпе келіпті"
other: "Соңғы кіруіңізден кейін %{count} ескертпе келіпті \U0001F418" other: "Соңғы кіруіңізден кейін %{count} ескертпе келіпті"
title: Сіз жоқ кезде... title: Сіз жоқ кезде...
favourite: favourite:
body: 'Жазбаңызды ұнатып, таңдаулыға қосты %{name}:' body: 'Жазбаңызды ұнатып, таңдаулыға қосты %{name}:'

View File

@ -725,8 +725,8 @@ ko:
one: 그리고, 접속 하지 않으신 동안 새 팔로워가 생겼습니다! one: 그리고, 접속 하지 않으신 동안 새 팔로워가 생겼습니다!
other: 게다가, 접속하지 않은 동안 %{count} 명의 팔로워가 생겼습니다! other: 게다가, 접속하지 않은 동안 %{count} 명의 팔로워가 생겼습니다!
subject: subject:
one: "1건의 새로운 알림 \U0001F418" one: "1건의 새로운 알림"
other: "%{count}건의 새로운 알림 \U0001F418" other: "%{count}건의 새로운 알림"
title: 당신이 없는 동안에... title: 당신이 없는 동안에...
favourite: favourite:
body: "%{name} 님이 내 툿을 즐겨찾기에 등록했습니다:" body: "%{name} 님이 내 툿을 즐겨찾기에 등록했습니다:"

View File

@ -675,9 +675,9 @@ lt:
one: Beje, jūs gavote naują sekėją, kol buvote atsijungęs! Yay! one: Beje, jūs gavote naują sekėją, kol buvote atsijungęs! Yay!
other: Beje, jūs gavote %{count} naujų sekėjų, nuo jūsų paskutinio apsilankymo! Nuostabu! other: Beje, jūs gavote %{count} naujų sekėjų, nuo jūsų paskutinio apsilankymo! Nuostabu!
subject: subject:
few: "%{count} nauji pranešimai, nuo paskutinio apsilankymo\U0001F418" few: "%{count} nauji pranešimai, nuo paskutinio apsilankymo"
one: "1 naujas pranešimas nuo paskutinio apsilankymo \U0001F418" one: "1 naujas pranešimas nuo paskutinio apsilankymo"
other: "%{count} nauji pranešimai, nuo paskutinio apsilankymo\U0001F418" other: "%{count} nauji pranešimai, nuo paskutinio apsilankymo"
title: Kol jūsų nebuvo... title: Kol jūsų nebuvo...
favourite: favourite:
body: 'Jūsų statusą pamėgo %{name}:' body: 'Jūsų statusą pamėgo %{name}:'

View File

@ -723,8 +723,8 @@ nl:
one: Je hebt trouwens sinds je weg was er ook een nieuwe volger bijgekregen! Hoera! one: Je hebt trouwens sinds je weg was er ook een nieuwe volger bijgekregen! Hoera!
other: Je hebt trouwens sinds je weg was er ook %{count} nieuwe volgers bijgekregen! Fantastisch! other: Je hebt trouwens sinds je weg was er ook %{count} nieuwe volgers bijgekregen! Fantastisch!
subject: subject:
one: "1 nieuwe melding sinds jouw laatste bezoek \U0001F418" one: "1 nieuwe melding sinds jouw laatste bezoek"
other: "%{count} nieuwe meldingen sinds jouw laatste bezoek \U0001F418" other: "%{count} nieuwe meldingen sinds jouw laatste bezoek"
title: Tijdens jouw afwezigheid... title: Tijdens jouw afwezigheid...
favourite: favourite:
body: 'Jouw toot werd door %{name} aan hun favorieten toegevoegd:' body: 'Jouw toot werd door %{name} aan hun favorieten toegevoegd:'

View File

@ -431,8 +431,8 @@
one: I tillegg har du fått en ny følger mens du var borte. Hurra! one: I tillegg har du fått en ny følger mens du var borte. Hurra!
other: I tillegg har du har fått %{count} nye følgere mens du var borte! Imponerende! other: I tillegg har du har fått %{count} nye følgere mens du var borte! Imponerende!
subject: subject:
one: "1 ny hendelse siden ditt siste besøk \U0001F418" one: "1 ny hendelse siden ditt siste besøk"
other: "%{count} nye hendelser siden ditt siste besøk \U0001F418" other: "%{count} nye hendelser siden ditt siste besøk"
title: I ditt fravær… title: I ditt fravær…
favourite: favourite:
body: 'Statusen din ble likt av %{name}:' body: 'Statusen din ble likt av %{name}:'

View File

@ -746,8 +746,8 @@ oc:
one: Avètz un nòu seguidor dempuèi vòstra darrièra visita! Ouà! one: Avètz un nòu seguidor dempuèi vòstra darrièra visita! Ouà!
other: Avètz %{count} nòus seguidors dempuèi vòstra darrièra visita! Qué crane! other: Avètz %{count} nòus seguidors dempuèi vòstra darrièra visita! Qué crane!
subject: subject:
one: "Una nòva notificacion dempuèi vòstra darrièra visita \U0001F418" one: "Una nòva notificacion dempuèi vòstra darrièra visita"
other: "%{count} nòvas notificacions dempuèi vòstra darrièra visita \U0001F418" other: "%{count} nòvas notificacions dempuèi vòstra darrièra visita"
title: Pendent vòstra abséncia… title: Pendent vòstra abséncia…
favourite: favourite:
body: "%{name} a mes vòstre estatut en favorit:" body: "%{name} a mes vòstre estatut en favorit:"

View File

@ -737,10 +737,10 @@ pl:
one: Dodatkowo, w czasie nieobecności zaczęła śledzić Cię jedna osoba Gratulacje! one: Dodatkowo, w czasie nieobecności zaczęła śledzić Cię jedna osoba Gratulacje!
other: Dodatkowo, zaczęło Cię śledzić %{count} nowych osób! Wspaniale! other: Dodatkowo, zaczęło Cię śledzić %{count} nowych osób! Wspaniale!
subject: subject:
few: "%{count} nowe powiadomienia od Twojej ostatniej wizyty \U0001F418" few: "%{count} nowe powiadomienia od Twojej ostatniej wizyty"
many: "%{count} nowych powiadomień od Twojej ostatniej wizyty \U0001F418" many: "%{count} nowych powiadomień od Twojej ostatniej wizyty"
one: "1 nowe powiadomienie od Twojej ostatniej wizyty \U0001F418" one: "1 nowe powiadomienie od Twojej ostatniej wizyty"
other: "%{count} nowych powiadomień od Twojej ostatniej wizyty \U0001F418" other: "%{count} nowych powiadomień od Twojej ostatniej wizyty"
title: W trakcie Twojej nieobecności… title: W trakcie Twojej nieobecności…
favourite: favourite:
body: 'Twój wpis został polubiony przez %{name}:' body: 'Twój wpis został polubiony przez %{name}:'

View File

@ -723,8 +723,8 @@ pt-BR:
one: Você tem um novo seguidor! Yay! one: Você tem um novo seguidor! Yay!
other: Você tem %{count} novos seguidores! Maravilha! other: Você tem %{count} novos seguidores! Maravilha!
subject: subject:
one: "Uma nova notificação desde o seu último acesso \U0001F418" one: "Uma nova notificação desde o seu último acesso"
other: "%{count} novas notificações desde o seu último acesso \U0001F418" other: "%{count} novas notificações desde o seu último acesso"
title: Enquanto você estava ausente... title: Enquanto você estava ausente...
favourite: favourite:
body: 'Sua postagem foi favoritada por %{name}:' body: 'Sua postagem foi favoritada por %{name}:'

View File

@ -665,8 +665,8 @@ pt:
one: Tens um novo seguidor! Boa! one: Tens um novo seguidor! Boa!
other: Tens %{count} novos seguidores! Fantástico! other: Tens %{count} novos seguidores! Fantástico!
subject: subject:
one: "1 nova notificação desde o último acesso \U0001F418" one: "1 nova notificação desde o último acesso"
other: "%{count} novas notificações desde o último acesso \U0001F418" other: "%{count} novas notificações desde o último acesso"
title: Enquanto estiveste ausente… title: Enquanto estiveste ausente…
favourite: favourite:
body: 'O teu post foi adicionado aos favoritos por %{name}:' body: 'O teu post foi adicionado aos favoritos por %{name}:'

View File

@ -743,10 +743,10 @@ ru:
one: Также, пока вас не было, у вас появился новый подписчик! Ура! one: Также, пока вас не было, у вас появился новый подписчик! Ура!
other: Также, пока вас не было, у вас появилось %{count} новых подписчиков! Отлично! other: Также, пока вас не было, у вас появилось %{count} новых подписчиков! Отлично!
subject: subject:
few: "%{count} новых уведомления с вашего последнего захода \U0001F418" few: "%{count} новых уведомления с вашего последнего захода"
many: "%{count} новых уведомлений с вашего последнего захода \U0001F418" many: "%{count} новых уведомлений с вашего последнего захода"
one: "1 новое уведомление с вашего последнего захода \U0001F418" one: "1 новое уведомление с вашего последнего захода"
other: "%{count} новых уведомлений с вашего последнего захода \U0001F418" other: "%{count} новых уведомлений с вашего последнего захода"
title: В ваше отсутствие… title: В ваше отсутствие…
favourite: favourite:
body: 'Ваш статус понравился %{name}:' body: 'Ваш статус понравился %{name}:'

View File

@ -703,9 +703,9 @@ sk:
one: Tiež si získal/a jedného nového následovateľa zatiaľ čo si bol/a preč. Yay! one: Tiež si získal/a jedného nového následovateľa zatiaľ čo si bol/a preč. Yay!
other: Tiež si získal/a %{count} nových následovateľov za tú dobu čo si bol/a preč. Yay! other: Tiež si získal/a %{count} nových následovateľov za tú dobu čo si bol/a preč. Yay!
subject: subject:
few: "%{count} nové notifikácie od tvojej poslednej návštevy \U0001F418" few: "%{count} nové notifikácie od tvojej poslednej návštevy"
one: "1 nové oboznámenie od tvojej poslednej návštevy \U0001F418" one: "1 nové oboznámenie od tvojej poslednej návštevy"
other: "%{count} nových oboznámení od tvojej poslednej návštevy \U0001F418" other: "%{count} nových oboznámení od tvojej poslednej návštevy"
title: Zatiaľ čo si bol/a preč… title: Zatiaľ čo si bol/a preč…
favourite: favourite:
body: 'Tvoj príspevok bol uložený medzi obľúbené užívateľa %{name}:' body: 'Tvoj príspevok bol uložený medzi obľúbené užívateľa %{name}:'

View File

@ -660,8 +660,8 @@ sq:
one: Veç kësaj, u bëtë me një ndjekës të ri, teksa sishit këtu! Ëhë! one: Veç kësaj, u bëtë me një ndjekës të ri, teksa sishit këtu! Ëhë!
other: Veç kësaj, u bëtë me %{count} ndjekës të rinj, teksa sishit këtu! Shkëlqyeshëm! other: Veç kësaj, u bëtë me %{count} ndjekës të rinj, teksa sishit këtu! Shkëlqyeshëm!
subject: subject:
one: "1 njoftim i ri që nga vizita juaj e fundit \U0001F418" one: "1 njoftim i ri që nga vizita juaj e fundit"
other: "%{count} 1 njoftime të reja që nga vizita juaj e fundit \U0001F418" other: "%{count} 1 njoftime të reja që nga vizita juaj e fundit"
title: Gjatë mungesës tuaj… title: Gjatë mungesës tuaj…
favourite: favourite:
body: 'Gjendja juaj u parapëlqye nga %{name}:' body: 'Gjendja juaj u parapëlqye nga %{name}:'

View File

@ -429,10 +429,10 @@ sr-Latn:
one: Dobili ste jednog novog pratioca! Jeee! one: Dobili ste jednog novog pratioca! Jeee!
other: Dobili ste %{count} novih pratioca! Sjajno! other: Dobili ste %{count} novih pratioca! Sjajno!
subject: subject:
few: "%{count} nova obaveštenja od poslednje posete \U0001F418" few: "%{count} nova obaveštenja od poslednje posete"
many: "%{count} novih obaveštenja od poslednje posete \U0001F418" many: "%{count} novih obaveštenja od poslednje posete"
one: "1 novo obaveštenje od poslednje posete \U0001F418" one: "1 novo obaveštenje od poslednje posete"
other: "%{count} novih obaveštenja od poslednje posete \U0001F418" other: "%{count} novih obaveštenja od poslednje posete"
favourite: favourite:
body: "%{name} je postavio kao omiljen Vaš status:" body: "%{name} je postavio kao omiljen Vaš status:"
subject: "%{name} je postavio kao omiljen Vaš status" subject: "%{name} je postavio kao omiljen Vaš status"

View File

@ -669,10 +669,10 @@ sr:
one: Добили сте једног новог пратиоца! Јеее! one: Добили сте једног новог пратиоца! Јеее!
other: Добили сте %{count} нових пратиоца! Сјајно! other: Добили сте %{count} нових пратиоца! Сјајно!
subject: subject:
few: "%{count} нова обавештења од последње посете \U0001F418" few: "%{count} нова обавештења од последње посете"
many: "%{count} нових обавештења од последње посете \U0001F418" many: "%{count} нових обавештења од последње посете"
one: "1 ново обавештење од последње посете \U0001F418" one: "1 ново обавештење од последње посете"
other: "%{count} нових обавештења од последње посете \U0001F418" other: "%{count} нових обавештења од последње посете"
title: Док нисте били ту... title: Док нисте били ту...
favourite: favourite:
body: "%{name} је поставио као омиљен Ваш статус:" body: "%{name} је поставио као омиљен Ваш статус:"

View File

@ -492,8 +492,8 @@ sv:
one: Du har också förvärvat en ny följare! Jippie! one: Du har också förvärvat en ny följare! Jippie!
other: Du har också fått %{count} nya följare medans du var iväg! Otroligt! other: Du har också fått %{count} nya följare medans du var iväg! Otroligt!
subject: subject:
one: "1 nytt meddelande sedan ditt senaste besök \U0001F418" one: "1 nytt meddelande sedan ditt senaste besök"
other: "%{count} nya meddelanden sedan ditt senaste besök \U0001F418" other: "%{count} nya meddelanden sedan ditt senaste besök"
title: I din frånvaro... title: I din frånvaro...
favourite: favourite:
body: 'Din status favoriserades av %{name}:' body: 'Din status favoriserades av %{name}:'

View File

@ -501,8 +501,8 @@ th:
one: นอกจากนี้คุณยังมีหนึ่งผู้ติดตามใหม่ขณะที่ไม่อยู่! เย่! one: นอกจากนี้คุณยังมีหนึ่งผู้ติดตามใหม่ขณะที่ไม่อยู่! เย่!
other: You have gotten %{count} new followers! Amazing! other: You have gotten %{count} new followers! Amazing!
subject: subject:
one: "1 new notification since your last visit \U0001F418" one: "1 new notification since your last visit"
other: "%{count} new notifications since your last visit \U0001F418" other: "%{count} new notifications since your last visit"
favourite: favourite:
body: 'สถานะของคุณได้รับการชื่นชอบโดย %{name}:' body: 'สถานะของคุณได้รับการชื่นชอบโดย %{name}:'
subject: "%{name} ได้ชื่นชอบสถานะของคุณ" subject: "%{name} ได้ชื่นชอบสถานะของคุณ"

View File

@ -303,8 +303,8 @@ tr:
one: Yeni bir takipçiniz var! one: Yeni bir takipçiniz var!
other: Yeni %{count} takipçiniz var! other: Yeni %{count} takipçiniz var!
subject: subject:
one: "Son ziyaretinizden beri 1 yeni bildiriminiz var \U0001F418" one: "Son ziyaretinizden beri 1 yeni bildiriminiz var"
other: "Son ziyaretinizden beri %{count} yeni bildiriminiz var \U0001F418" other: "Son ziyaretinizden beri %{count} yeni bildiriminiz var"
favourite: favourite:
body: "%{name} durumunuzu favorilere ekledi:" body: "%{name} durumunuzu favorilere ekledi:"
subject: "%{name} favorilere ekledi" subject: "%{name} favorilere ekledi"

View File

@ -538,10 +538,10 @@ uk:
one: Також, у Вас з'явився новий підписник, коли ви були відсутні! Ура! one: Також, у Вас з'явився новий підписник, коли ви були відсутні! Ура!
other: Також, у Вас з'явилось %{count} нових підписників, поки ви були відсутні! Чудово! other: Також, у Вас з'явилось %{count} нових підписників, поки ви були відсутні! Чудово!
subject: subject:
few: "%{count} нові сповіщення з Вашого останнього входу \U0001F418" few: "%{count} нові сповіщення з Вашого останнього входу"
many: "%{count} нових сповіщень з Вашого останнього входу \U0001F418" many: "%{count} нових сповіщень з Вашого останнього входу"
one: "1 нове сповіщення з Вашого останнього входу \U0001F418" one: "1 нове сповіщення з Вашого останнього входу"
other: "%{count} нових сповіщень з Вашого останнього входу \U0001F418" other: "%{count} нових сповіщень з Вашого останнього входу"
title: Поки ви були відсутні... title: Поки ви були відсутні...
favourite: favourite:
body: 'Ваш статус подобається %{name}:' body: 'Ваш статус подобається %{name}:'

View File

@ -562,7 +562,7 @@ zh-CN:
new_followers_summary: new_followers_summary:
one: 而且,你不在的时候,有一个人关注了你!耶! one: 而且,你不在的时候,有一个人关注了你!耶!
other: 而且,你不在的时候,有 %{count} 个人关注了你!好棒! other: 而且,你不在的时候,有 %{count} 个人关注了你!好棒!
subject: "自从你最后一次登录以来,你错过了 %{count} 条新通知 \U0001F418" subject: "自从你最后一次登录以来,你错过了 %{count} 条新通知"
title: 在你不在的这段时间…… title: 在你不在的这段时间……
favourite: favourite:
body: 你的嘟文被 %{name} 收藏了: body: 你的嘟文被 %{name} 收藏了:

View File

@ -490,8 +490,8 @@ zh-HK:
one: 你新獲得了 1 位關注者了!恭喜! one: 你新獲得了 1 位關注者了!恭喜!
other: 你新獲得了 %{count} 位關注者了!好厲害! other: 你新獲得了 %{count} 位關注者了!好厲害!
subject: subject:
one: "自從上次登入以來,你收到 1 則新的通知 \U0001F418" one: "自從上次登入以來,你收到 1 則新的通知"
other: "自從上次登入以來,你收到 %{count} 則新的通知 \U0001F418" other: "自從上次登入以來,你收到 %{count} 則新的通知"
title: 在你不在的這段時間…… title: 在你不在的這段時間……
favourite: favourite:
body: 您的文章被 %{name} 收藏: body: 您的文章被 %{name} 收藏:

View File

@ -567,7 +567,7 @@ zh-TW:
body: 以下是自%{since}你最後一次登入以來錯過的訊息摘要 body: 以下是自%{since}你最後一次登入以來錯過的訊息摘要
mention: "%{name} 在此提及了你:" mention: "%{name} 在此提及了你:"
new_followers_summary: 而且,你不在的時候,有 %{count} 個人關注你了! 好棒! new_followers_summary: 而且,你不在的時候,有 %{count} 個人關注你了! 好棒!
subject: "自從上次登入以來,你收到 %{count} 則新的通知 \U0001F418" subject: "自從上次登入以來,你收到 %{count} 則新的通知"
title: 你不在的時候... title: 你不在的時候...
favourite: favourite:
body: '你的嘟文被 %{name} 加入了最愛:' body: '你的嘟文被 %{name} 加入了最愛:'

View File

@ -446,6 +446,15 @@ Rails.application.routes.draw do
get '/about/dmca', to: 'about#dmca' get '/about/dmca', to: 'about#dmca'
get '/about/sales', to: 'about#sales' get '/about/sales', to: 'about#sales'
get '/tags/:tag', to: 'home#index'
get '/:username', to: 'home#index', as: :short_account
get '/:username/with_replies', to: 'home#index', as: :short_account_with_replies
get '/:username/media', to: 'home#index', as: :short_account_media
get '/:username/tagged/:tag', to: 'home#index', as: :short_account_tag
get '/:username/posts/:statusId/reblogs', to: 'home#index'
get '/:account_username/posts/:id', to: 'home#index', as: :short_account_status
get '/:account_username/posts/:id/embed', to: 'statuses#embed', as: :embed_short_account_status
get '/(*any)', to: 'home#index', as: :web get '/(*any)', to: 'home#index', as: :web
root 'home#index' root 'home#index'
@ -454,13 +463,6 @@ Rails.application.routes.draw do
get '/explore', to: 'directories#index', as: :explore get '/explore', to: 'directories#index', as: :explore
get '/explore/:id', to: 'directories#show', as: :explore_hashtag get '/explore/:id', to: 'directories#show', as: :explore_hashtag
get '/:username', to: 'accounts#show', as: :short_account
get '/:username/with_replies', to: 'accounts#show', as: :short_account_with_replies
get '/:username/media', to: 'accounts#show', as: :short_account_media
get '/:username/tagged/:tag', to: 'accounts#show', as: :short_account_tag
get '/:account_username/posts/:id', to: 'statuses#show', as: :short_account_status
get '/:account_username/posts/:id/embed', to: 'statuses#embed', as: :embed_short_account_status
resources :tags, only: [:show] resources :tags, only: [:show]
match '*unmatched_route', match '*unmatched_route',