diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index b498a1ed..3cdc9fb9 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -4,6 +4,7 @@ class HomeController < ApplicationController before_action :authenticate_user! before_action :set_referrer_policy_header before_action :set_initial_state_json + before_action :set_data_for_meta def index @body_classes = 'app-body' @@ -11,17 +12,40 @@ class HomeController < ApplicationController 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! return if user_signed_in? # 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 matches + if find_route_matches redirect_to(homepage_path) 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 serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) @initial_state_json = serializable_resource.to_json diff --git a/app/javascript/gabsocial/actions/sidebar.js b/app/javascript/gabsocial/actions/sidebar.js new file mode 100644 index 00000000..bd646796 --- /dev/null +++ b/app/javascript/gabsocial/actions/sidebar.js @@ -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, + }; +}; diff --git a/app/javascript/gabsocial/actions/statuses.js b/app/javascript/gabsocial/actions/statuses.js index 1b2744c4..06b40542 100644 --- a/app/javascript/gabsocial/actions/statuses.js +++ b/app/javascript/gabsocial/actions/statuses.js @@ -3,7 +3,7 @@ import openDB from '../storage/db'; import { evictStatus } from '../storage/modifier'; import { deleteFromTimelines } from './timelines'; import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer'; -import { ensureComposeIsVisible } from './compose'; +import { openModal } from './modal'; import { me } from 'gabsocial/initial_state'; export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; @@ -159,7 +159,7 @@ export function deleteStatus(id, routerHistory, withRedraft = false) { if (withRedraft) { dispatch(redraft(status, response.data.text)); - ensureComposeIsVisible(getState, routerHistory); + dispatch(openModal('COMPOSE')); } }).catch(error => { dispatch(deleteStatusFail(id, error)); @@ -272,7 +272,7 @@ export function muteStatusFail(id, error) { export function unmuteStatus(id) { return (dispatch, getState) => { if (!me) return; - + dispatch(unmuteStatusRequest(id)); api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => { diff --git a/app/javascript/gabsocial/components/sidebar_menu.js b/app/javascript/gabsocial/components/sidebar_menu.js new file mode 100644 index 00000000..410eae55 --- /dev/null +++ b/app/javascript/gabsocial/components/sidebar_menu.js @@ -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 ( +