This commit is contained in:
mgabdev 2020-05-03 01:22:49 -04:00
parent 196a906cec
commit 055b5a430f
85 changed files with 1110 additions and 1051 deletions

@ -4,52 +4,80 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
include Authorization
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
respond_to :json
before_action :set_status
before_action :verify_own_status
after_action :insert_pagination_headers
def index
render json: {}, status: :ok
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
end
private
def load_accounts
#
scope = default_accounts
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
scope.merge(paginated_favourites).to_a
end
def default_accounts
#
Account
.includes(:favourites, :account_stat)
.references(:favourites)
.where(favourites: { status_id: @status.id })
end
def paginated_favourites
#
Favourite.paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT),
params[:max_id],
params[:since_id]
)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
#
if records_continue?
api_v1_status_favourited_by_index_url pagination_params(max_id: pagination_max_id)
end
end
def prev_path
#
unless @accounts.empty?
api_v1_status_favourited_by_index_url pagination_params(since_id: pagination_since_id)
end
end
def pagination_max_id
#
@accounts.last.favourites.last.id
end
def pagination_since_id
#
@accounts.first.favourites.first.id
end
def records_continue?
#
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
end
def set_status
#
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
def pagination_params(core_params)
#
params.slice(:limit).permit(:limit).merge(core_params)
end
end
def verify_own_status
unless @status.account.id == current_account.id
render json: {}, status: 404
end
end
end

@ -99,7 +99,7 @@ class ApplicationController < ActionController::Base
end
def current_theme
'light'
'default'
# : todo :
# current_user.setting_theme
end

@ -258,8 +258,7 @@ export const fetchListSuggestions = (q) => (dispatch, getState) => {
const params = {
q,
resolve: false,
limit: 4,
following: true,
limit: 25,
};
api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => {

@ -4,6 +4,7 @@ import { me } from '../initial_state'
export const GIFS_CLEAR_RESULTS = 'GIFS_CLEAR_RESULTS'
export const GIF_SET_SELECTED = 'GIF_SET_SELECTED'
export const GIF_CHANGE_SEARCH_TEXT = 'GIF_CHANGE_SEARCH_TEXT'
export const GIF_CLEAR_SELECTED = 'GIF_CLEAR_SELECTED'
export const GIF_RESULTS_FETCH_REQUEST = 'GIF_RESULTS_FETCH_REQUEST'
export const GIF_RESULTS_FETCH_SUCCESS = 'GIF_RESULTS_FETCH_SUCCESS'

@ -135,6 +135,8 @@ class Account extends ImmutablePureComponent {
isOutline={true}
color='brand'
backgroundColor='none'
icon={actionIcon}
iconSize='10px'
>
{actionTitle}
</Button>

@ -265,7 +265,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
className={textareaClasses}
disabled={disabled}
placeholder={placeholder}
autoFocus={false}
dautoFocus={false}
value={value}
onChange={this.onChange}
// onKeyDown={this.onKeyDown}

@ -138,6 +138,7 @@ export default class Button extends PureComponent {
cursorPointer: 1,
textAlignCenter: 1,
outlineNone: 1,
// outlineOnFocus: !isText,
flexRow: !!children && !!icon,
cursorNotAllowed: isDisabled,
opacity05: isDisabled,
@ -221,6 +222,9 @@ export default class Button extends PureComponent {
)
}
const isLogout = href === '/auth/sign_out'
const dataMethod = isLogout ? 'delete' : undefined
const options = {
rel,
target,
@ -230,6 +234,7 @@ export default class Button extends PureComponent {
className: classes,
href: href || undefined,
ref: this.setRef,
'data-method': dataMethod,
...handlers,
}

@ -1,7 +1,4 @@
import classnames from 'classnames/bind'
// Bind CSS Modules global variable `_s` to classNames module
const cx = classnames.bind(_s)
import { CX } from '../constants'
/**
* Renders a divider component
@ -18,7 +15,7 @@ export default class Divider extends PureComponent {
render() {
const { isSmall, isInvisible } = this.props
const classes = cx({
const classes = CX({
default: 1,
borderBottom1PX: !isInvisible,
borderColorSecondary: !isInvisible,

@ -31,13 +31,13 @@ export default class Heading extends PureComponent {
}
render() {
const { children, size, center } = this.props
const { children, size, isCentered } = this.props
// Each size has it's own custom style
const classes = cx({
default: 1,
text: 1,
textAlignCenter: center,
textAlignCenter: isCentered,
colorPrimary: [SIZES.h1, SIZES.h2].indexOf(size) > -1,
colorSecondary: [SIZES.h3, SIZES.h4, SIZES.h5].indexOf(size) > -1,

@ -54,11 +54,12 @@ class LinkFooter extends PureComponent {
text: intl.formatMessage(messages.help),
requiresUser: true,
},
{
onClick: onOpenHotkeys,
text: intl.formatMessage(messages.hotkeys),
requiresUser: true,
},
// : todo :
// {
// onClick: onOpenHotkeys,
// text: intl.formatMessage(messages.hotkeys),
// requiresUser: true,
// },
{
to: '/auth/edit',
text: intl.formatMessage(messages.security),

@ -56,6 +56,8 @@ export default class ListItem extends PureComponent {
flexRow: 1,
alignItemsCenter: 1,
width100PC: 1,
outlineNone: 1,
bgTransparent: 1,
bgSubtle_onHover: 1,
borderColorSecondary: !isLast,
borderBottom1PX: !isLast,

@ -8,7 +8,7 @@ import SettingSwitch from '../setting_switch'
import Text from '../text'
const messages = defineMessages({
title: { id: 'community_timeline_settings', defaultMessage: 'Community Timeline Settings' },
title: { id: 'community_timeline_settings', defaultMessage: 'Community Feed Settings' },
saveAndClose: { id: 'saveClose', defaultMessage: 'Save & Close' },
onlyMedia: { id: 'community.column_settings.media_only', defaultMessage: 'Media Only' },
showInSidebar: { id: 'show_in_sidebar', defaultMessage: 'Show in Sidebar' },
@ -18,17 +18,15 @@ const mapStateToProps = (state) => ({
settings: state.getIn(['settings', 'community']),
})
const mapDispatchToProps = (dispatch, { onClose }) => {
return {
onChange(key, checked) {
dispatch(changeSetting(['community', ...key], checked))
},
onSave() {
dispatch(saveSettings())
onClose()
},
}
}
const mapDispatchToProps = (dispatch, { onClose }) => ({
onChange(key, checked) {
dispatch(changeSetting(['community', ...key], checked))
},
onSave() {
dispatch(saveSettings())
onClose()
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@ -39,6 +37,7 @@ class CommunityTimelineSettingsModal extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
}
@ -47,10 +46,16 @@ class CommunityTimelineSettingsModal extends ImmutablePureComponent {
}
render() {
const { intl, settings, onChange, onClose } = this.props
const {
intl,
settings,
onChange,
onClose,
} = this.props
return (
<ModalLayout
onClose={onClose}
width={320}
title={intl.formatMessage(messages.title)}
>

@ -14,8 +14,6 @@ class ConfirmationModal extends PureComponent {
confirm: PropTypes.any.isRequired,
onClose: PropTypes.func.isRequired,
onConfirm: PropTypes.func.isRequired,
secondary: PropTypes.string,
onSecondary: PropTypes.func,
intl: PropTypes.object.isRequired,
onCancel: PropTypes.func,
}
@ -29,11 +27,6 @@ class ConfirmationModal extends PureComponent {
this.props.onConfirm()
}
handleSecondary = () => {
this.props.onClose()
this.props.onSecondary()
}
handleCancel = () => {
const { onClose, onCancel } = this.props
onClose()
@ -49,7 +42,6 @@ class ConfirmationModal extends PureComponent {
title,
message,
confirm,
secondary
} = this.props
return (
@ -74,19 +66,11 @@ class ConfirmationModal extends PureComponent {
onClick={this.handleCancel}
className={[_s.mr10, _s.flexGrow1].join(' ')}
>
<Text size='medium' weight='bold' color='inherit'>
<Text size='medium' weight='bold' align='center' color='inherit'>
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
</Text>
</Button>
{ /**
: todo :
*/
secondary !== undefined && (
<Button text={secondary} onClick={this.handleSecondary} />
)
}
<Button
backgroundColor='brand'
color='white'
@ -94,7 +78,7 @@ class ConfirmationModal extends PureComponent {
ref={this.setRef}
className={_s.flexGrow1}
>
<Text size='medium' weight='bold' color='inherit'>
<Text size='medium' weight='bold' align='center' color='inherit'>
{confirm}
</Text>
</Button>

@ -1,6 +1,12 @@
import { injectIntl, defineMessages } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { changeSetting, saveSettings } from '../../actions/settings'
import {
DEFAULT_THEME,
DEFAULT_FONT_SIZE,
FONT_SIZES,
} from '../../constants'
import ModalLayout from './modal_layout'
import Button from '../button'
import Text from '../text'
@ -12,12 +18,15 @@ const messages = defineMessages({
})
const mapStateToProps = (state) => ({
settings: state.getIn(['notifications', 'filter']),
displayOptionsSettings: state.getIn(['settings', 'displayOptions']),
fontSize: state.getIn(['settings', 'displayOptions', 'fontSize'], DEFAULT_FONT_SIZE),
theme: state.getIn(['settings', 'displayOptions', 'theme'], DEFAULT_THEME),
})
const mapDispatchToProps = (dispatch) => ({
onChange(path, value) {
dispatch(setFilter(path, value))
onChange(key, value) {
dispatch(changeSetting(['displayOptions', key], value))
dispatch(saveSettings())
},
})
@ -27,34 +36,47 @@ export default
class DisplayOptionsModal extends ImmutablePureComponent {
static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
fontSize: PropTypes.string,
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
displayOptionsSettings: ImmutablePropTypes.map,
theme: PropTypes.string,
onChange: PropTypes.func.isRequired,
settings: ImmutablePropTypes.map.isRequired,
onClose: PropTypes.func.isRequired,
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
updateOnProps = [
'fontSize',
'displayOptionsSettings',
'theme',
]
handleOnFontSizeChange = (e) => {
const fontSizeNames = Object.keys(FONT_SIZES)
const index = fontSizeNames[e.target.value]
this.props.onChange('fontSize', index)
}
handleOnThemeSelected = (e) => {
this.props.onChange('theme', e.target.value)
}
handleOnRadiusKeyDisabled = (key, checked) => {
this.props.onChange(key, checked)
}
// document.documentElement.style.setProperty("--color-surface", "black");
render() {
const {
account,
fontSize,
displayOptionsSettings,
intl,
settings,
onChange,
theme,
onClose,
} = this.props
// theme - light, muted, dark
// text size - extra small, small, normal, medium, large, extra large
// rounded borders
const fontSizeNames = Object.keys(FONT_SIZES)
const currentFontSizeIndex = fontSizeNames.indexOf(fontSize)
return (
<ModalLayout
@ -64,86 +86,165 @@ class DisplayOptionsModal extends ImmutablePureComponent {
>
<div className={[_s.default, _s.mb15].join(' ')}>
<Text>
<Text align='center' color='secondary' size='medium'>
{intl.formatMessage(messages.message)}
</Text>
</div>
<div className={[_s.default, _s.mb10].join(' ')}>
<Text weight='bold' color='secondary'>
<div className={[_s.default, _s.mb15].join(' ')}>
<Text weight='bold' size='small' color='secondary'>
Font Size
</Text>
<div className={[_s.default, _s.radiusSmall, _s.mt10, _s.py15, _s.px15, _s.bgTertiary].join(' ')}>
test
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<span className={[_s.default, _s.text, _s.colorPrimary].join(' ')} style={{fontSize: '12px'}}>
Aa
</span>
<input
type='range'
min='0'
value={currentFontSizeIndex}
max={fontSizeNames.length - 1}
onInput={this.handleOnFontSizeChange}
onChange={this.handleOnFontSizeChange}
className={[_s.flexGrow1, _s.outlineNone, _s.ml15, _s.mr15].join(' ')}
/>
<span className={[_s.default, _s.text, _s.colorPrimary].join(' ')} style={{fontSize: '18px'}}>
Aa
</span>
</div>
</div>
</div>
<div className={[_s.default, _s.mb10].join(' ')}>
<Text weight='bold' color='secondary'>
<div className={[_s.default, _s.mb15].join(' ')}>
<Text weight='bold' size='small' color='secondary'>
Rounded
</Text>
<div className={[_s.default, _s.radiusSmall, _s.mt10, _s.py15, _s.px15, _s.bgTertiary].join(' ')}>
<SettingSwitch
prefix='notification'
settings={settings}
settingPath={'onlyVerified'}
onChange={onChange}
label={'Small Radius'}
prefix='displayOptions'
settings={displayOptionsSettings}
settingPath={'radiusSmallDisabled'}
onChange={this.handleOnRadiusKeyDisabled}
label={'Small Radius Disabled'}
/>
<SettingSwitch
prefix='notification'
settings={settings}
settingPath={'onlyVerified'}
onChange={onChange}
label={'Circle Radius'}
prefix='displayOptions'
settings={displayOptionsSettings}
settingPath={'radiusCircleDisabled'}
onChange={this.handleOnRadiusKeyDisabled}
label={'Circles Disabled'}
/>
</div>
</div>
<div className={[_s.default, _s.mb10].join(' ')}>
<Text weight='bold' color='secondary'>
<Text weight='bold' size='small' color='secondary'>
Theme
</Text>
<div className={[_s.default, _s.radiusSmall, _s.flexRow, _s.mt10, _s.py15, _s.px15, _s.bgTertiary].join(' ')}>
<div className={[_s.default, _s.radiusSmall, _s.flexRow, _s.mt10, _s.py10, _s.bgTertiary].join(' ')}>
<div className={[_s.default, _s.py10, _s.px10, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.bgPrimary, _s.radiusSmall]}>
<Text>
Light
</Text>
</div>
</div>
<ThemeBlock
title='Light'
value='light'
checked={theme === 'light'}
onChange={this.handleOnThemeSelected}
style={{
borderColor: '#ececed',
backgroundColor: '#fff',
color: '#2d3436',
}}
/>
<div className={[_s.default, _s.py10, _s.px10, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.bgPrimary, _s.radiusSmall]}>
<Text>
Muted
</Text>
</div>
</div>
<ThemeBlock
title='Muted'
value='muted'
checked={theme === 'muted'}
onChange={this.handleOnThemeSelected}
style={{
borderColor: '#424141',
backgroundColor: '#222',
color: '#fff',
}}
/>
<div className={[_s.default, _s.py10, _s.px10, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.bgPrimary, _s.radiusSmall]}>
<Text>
Black
</Text>
</div>
</div>
<ThemeBlock
title='Black'
value='black'
checked={theme === 'black'}
onChange={this.handleOnThemeSelected}
style={{
borderColor: '#212020',
backgroundColor: '#13171b',
color: '#cccbcb',
}}
/>
</div>
</div>
<div className={[_s.mlAuto, _s.mrAuto].join(' ')}>
<Button>
<Text size='medium' color='inherit'>
<div className={[_s.mlAuto, _s.my10, _s.mrAuto].join(' ')}>
<Button onClick={onClose}>
<Text size='medium' color='inherit' className={_s.px10}>
Done
</Text>
</Button>
</div>
</ModalLayout>
</ModalLayout>
)
}
}
class ThemeBlock extends PureComponent {
static propTypes = {
checked: PropTypes.bool,
onChange: PropTypes.func.isRequired,
title: PropTypes.string,
value: PropTypes.string,
style: PropTypes.object,
}
render() {
const {
checked,
onChange,
title,
value,
style,
} = this.props
const id = `theme-${value}`
return (
<label className={[_s.default, _s.px10, _s.flexGrow1].join(' ')} htmlFor={id}>
<div
className={[_s.default, _s.borderBottom6PX, _s.alignItemsCenter, _s.flexRow, _s.py10, _s.px15, _s.radiusSmall].join(' ')}
style={style}
>
<input
type='radio'
name='theme'
value={value}
id={id}
checked={checked}
onChange={onChange}
/>
<Text
align='center'
size='medium'
weight='bold'
color='inherit'
className={[_s.py10, _s.flexGrow1].join(' ')}
>
{title}
</Text>
</div>
</label>
)
}
}

@ -1,3 +1,5 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import { deleteList } from '../../actions/lists'
import ConfirmationModal from './confirmation_modal'
@ -9,24 +11,24 @@ const messages = defineMessages({
})
const mapDispatchToProps = (dispatch) => ({
onConfirm(listId) {
dispatch(deleteList(listId))
},
onConfirm: (listId) => dispatch(deleteList(listId)),
})
export default
@injectIntl
@connect(null, mapDispatchToProps)
class ListDeleteModal extends PureComponent {
class ListDeleteModal extends ImmutablePureComponent {
static propTypes = {
list: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
list: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
}
handleClick = () => {
this.props.onConfirm(this.props.list.get('id'))
// : todo :
// redirect back to /lists
}
render() {

@ -14,19 +14,20 @@ class ListEditorModal extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
listId: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
}
render() {
const { intl, onClose, listId } = this.props
const { intl, onClose, id } = this.props
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width={500}
onClose={onClose}
noPadding
>
<ListEdit listId={listId} />
<ListEdit id={id} />
</ModalLayout>
)
}

@ -21,17 +21,15 @@ const mapStateToProps = (state) => ({
settings: state.getIn(['settings', 'list']),
})
const mapDispatchToProps = (dispatch, { onClose }) => {
return {
onChange(key, checked) {
dispatch(changeSetting(['list', ...key], checked))
},
onSave() {
dispatch(saveSettings())
onClose()
},
}
}
const mapDispatchToProps = (dispatch, { onClose }) => ({
onChange(key, checked) {
dispatch(changeSetting(['list', ...key], checked))
},
onSave() {
dispatch(saveSettings())
onClose()
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)

@ -29,6 +29,8 @@ import {
MODAL_MUTE,
MODAL_PRO_UPGRADE,
MODAL_REPORT,
MODAL_STATUS_LIKES,
MODAL_STATUS_REPOSTS,
MODAL_STATUS_REVISIONS,
MODAL_UNAUTHORIZED,
MODAL_UNFOLLOW,
@ -60,6 +62,8 @@ import {
MuteModal,
ProUpgradeModal,
ReportModal,
StatusLikesModal,
StatusRepostsModal,
StatusRevisionsModal,
UnauthorizedModal,
UnfollowModal,
@ -92,6 +96,8 @@ MODAL_COMPONENTS[MODAL_MEDIA] = MediaModal
MODAL_COMPONENTS[MODAL_MUTE] = MuteModal
MODAL_COMPONENTS[MODAL_PRO_UPGRADE] = ProUpgradeModal
MODAL_COMPONENTS[MODAL_REPORT] = ReportModal
MODAL_COMPONENTS[MODAL_STATUS_LIKES] = StatusLikesModal
MODAL_COMPONENTS[MODAL_STATUS_REPOSTS] = StatusRepostsModal
MODAL_COMPONENTS[MODAL_STATUS_REVISIONS] = StatusRevisionsModal
MODAL_COMPONENTS[MODAL_UNAUTHORIZED] = UnauthorizedModal
MODAL_COMPONENTS[MODAL_UNFOLLOW] = UnfollowModal

@ -0,0 +1,44 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import StatusLikes from '../../features/status_likes'
import ModalLayout from './modal_layout'
const messages = defineMessages({
title: { id: 'likes', defaultMessage: 'Likes' },
})
export default
@injectIntl
class StatusLikesModal extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
status: ImmutablePropTypes.map.isRequired,
}
render() {
const {
intl,
onClose,
status,
} = this.props
const params = {
statusId: status.get('id'),
}
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width={460}
onClose={onClose}
noPadding
>
<StatusLikes params={params} />
</ModalLayout>
)
}
}

@ -0,0 +1,44 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import StatusReposts from '../../features/status_reposts'
import ModalLayout from './modal_layout'
const messages = defineMessages({
title: { id: 'reposts', defaultMessage: 'Reposts' },
})
export default
@injectIntl
class StatusRepostsModal extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
status: ImmutablePropTypes.map.isRequired,
}
render() {
const {
intl,
onClose,
status,
} = this.props
const params = {
statusId: status.get('id'),
}
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width={460}
onClose={onClose}
noPadding
>
<StatusReposts params={params} />
</ModalLayout>
)
}
}

@ -1,68 +0,0 @@
import { injectIntl, defineMessages } from 'react-intl'
import { muteAccount } from '../../actions/accounts'
const messages = defineMessages({
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' },
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
confirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
})
const mapStateToProps = (state) => ({
isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
account: state.getIn(['mutes', 'new', 'account']),
})
const mapDispatchToProps = (dispatch) => ({
onConfirm(account, notifications) {
dispatch(muteAccount(account.get('id'), notifications))
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class UnfollowModal extends PureComponent {
static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
componentDidMount() {
this.button.focus()
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
}
handleCancel = () => {
this.props.onClose()
}
render() {
const { account, intl } = this.props
// , {
// message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
// confirm: intl.formatMessage(messages.unfollowConfirm),
// onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
// }));
return (
<ConfirmationModal
title={`Mute @${account.get('acct')}`}
message={<FormattedMessage id='confirmations.mute.message' defaultMessage='Are you sure you want to mute @{name}?' values={{ name: account.get('acct') }} />}
confirm={<FormattedMessage id='mute' defaultMessage='Mute' />}
onConfirm={() => {
// dispatch(blockDomain(domain))
// dispatch(blockDomain(domain))
}}
/>
)
}
}

@ -1,10 +1,11 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import moment from 'moment-mini'
import PanelLayout from './panel_layout'
import ColumnIndicator from '../column_indicator'
import Divider from '../divider'
import Icon from '../icon'
import RelativeTimestamp from '../relative_timestamp'
import Text from '../text'
const messages = defineMessages({
@ -18,16 +19,20 @@ class ListDetailsPanel extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onEdit: PropTypes.func.isRequired,
list: ImmutablePropTypes.map,
onEdit: PropTypes.func.isRequired,
}
handleOnEdit = () => {
this.props.onEdit()
}
updateOnProps = [
'list',
]
render() {
const { intl, list } = this.props
const {
intl,
list,
onEdit,
} = this.props
const title = !!list ? list.get('title') : ''
const createdAt = !!list ? list.get('created_at') : ''
@ -36,34 +41,41 @@ class ListDetailsPanel extends ImmutablePureComponent {
<PanelLayout
title={intl.formatMessage(messages.title)}
headerButtonTitle={intl.formatMessage(messages.edit)}
headerButtonAction={this.handleOnEdit}
headerButtonAction={onEdit}
>
<div className={_s.default}>
{
(!title || !createdAt) &&
<ColumnIndicator type='loading' />
}
{
title && createdAt &&
<div className={_s.default}>
<div className={_s.default}>
<Text weight='medium'>
{title}
</Text>
</div>
<Divider isSmall />
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' size='12px' className={_s.fillSecondary} />
<Text
size='small'
color='secondary'
className={_s.ml5}
>
{
<FormattedMessage id='lists.panel_created' defaultMessage='Created: {date}' values={{
date: moment(createdAt).format('lll'),
}} />
}
</Text>
</div>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Text weight='medium'>
{title}
</Text>
</div>
<Divider isSmall />
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' size='12px' className={_s.fillSecondary} />
<Text
size='small'
color='secondary'
className={_s.ml5}
>
{
<FormattedMessage id='lists.panel_created' defaultMessage='Created: {date}' values={{
date: <RelativeTimestamp timestamp={createdAt} />,
}} />
}
</Text>
</div>
</div>
}
</PanelLayout>
)
}

@ -1,6 +1,7 @@
import { defineMessages, injectIntl } from 'react-intl'
import { MODAL_DISPLAY_OPTIONS } from '../../constants'
import { openModal } from '../../actions/modal'
import { closePopover } from '../../actions/popover'
import PopoverLayout from './popover_layout'
import List from '../list'
@ -13,6 +14,7 @@ const messages = defineMessages({
const mapDispatchToProps = (dispatch) => ({
onOpenDisplayModal: () => {
dispatch(closePopover())
dispatch(openModal(MODAL_DISPLAY_OPTIONS))
},
})
@ -54,7 +56,7 @@ class SidebarMorePopover extends PureComponent {
},
{
title: intl.formatMessage(messages.logout),
href: '/auth/log_out',
href: '/auth/sign_out',
},
]}
/>

@ -2,6 +2,7 @@ import throttle from 'lodash.throttle'
import { List as ImmutableList } from 'immutable'
import IntersectionObserverArticle from './intersection_observer_article'
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'
import Block from './block'
import ColumnIndicator from './column_indicator'
import LoadMore from './load_more'
@ -250,7 +251,11 @@ export default class ScrollableList extends PureComponent {
);
}
return <ColumnIndicator type='error' message={emptyMessage} />
return (
<Block>
<ColumnIndicator type='error' message={emptyMessage} />
</Block>
)
}
}

@ -147,6 +147,7 @@ class Search extends PureComponent {
onChange={this.handleOnChange}
onFocus={this.handleOnFocus}
onBlur={this.handleOnBlur}
autoComplete='off'
/>
<Button

@ -16,6 +16,14 @@ export default class SettingSwitch extends ImmutablePureComponent {
onChange: PropTypes.func.isRequired,
}
updateOnProps = [
'prefix',
'settings',
'settingPath',
'description',
'label',
]
onChange = ({ target }) => {
this.props.onChange(this.props.settingPath, target.checked)
}

@ -38,17 +38,13 @@ const messages = defineMessages({
donate: { id: 'tabs_bar.donate', defaultMessage: 'Make a Donation' },
})
const mapStateToProps = (state) => {
const getAccount = makeGetAccount()
return {
account: getAccount(state, me),
moreOpen: state.getIn(['popover', 'popoverType']) === 'SIDEBAR_MORE',
notificationCount: state.getIn(['notifications', 'unread']),
homeItemsQueueCount: state.getIn(['timelines', 'home', 'totalQueuedItemsCount']),
showCommunityTimeline: state.getIn(['settings', 'community', 'shows', 'inSidebar']),
}
}
const mapStateToProps = (state) => ({
account: makeGetAccount()(state, me),
moreOpen: state.getIn(['popover', 'popoverType']) === 'SIDEBAR_MORE',
notificationCount: state.getIn(['notifications', 'unread']),
homeItemsQueueCount: state.getIn(['timelines', 'home', 'totalQueuedItemsCount']),
showCommunityTimeline: state.getIn(['settings', 'community', 'shows', 'inSidebar']),
})
const mapDispatchToProps = (dispatch) => ({
onClose() {
@ -91,7 +87,7 @@ class Sidebar extends ImmutablePureComponent {
this.props.onOpenComposeModal()
}
handleOpenSidebarMorePopover =() => {
handleOpenSidebarMorePopover = () => {
this.props.openSidebarMorePopover({
targetRef: this.moreBtnRef,
position: 'top',
@ -226,23 +222,46 @@ class Sidebar extends ImmutablePureComponent {
<div className={[_s.default, _s.width240PX].join(' ')}>
<div className={[_s.default, _s.posFixed, _s.heightCalc53PX, _s.bottom0].join(' ')}>
<div className={[_s.default, _s.height100PC, _s.alignItemsStart, _s.width240PX, _s.pr15, _s.py10, _s.overflowYScroll].join(' ')}>
<div className={_s.default}>
<div className={[_s.default, _s.width100PC].join(' ')}>
{
!!title &&
<div className={[_s.default, _s.flexRow, _s.px5, _s.py10].join(' ')}>
<Button
noClasses
color='primary'
backgroundColor='none'
className={[_s.alignItemsCenter, _s.bgTransparent, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.default, _s.justifyContentCenter].join(' ')}
icon='back'
iconSize='20px'
iconClassName={[_s.mr5, _s.fillPrimary].join(' ')}
onClick={this.handleBackClick}
/>
<div className={[_s.default, _s.flexRow, _s.px5, _s.pt10].join(' ')}>
{
showBackBtn &&
<Button
noClasses
color='primary'
backgroundColor='none'
className={[_s.alignItemsCenter, _s.bgTransparent, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.default, _s.justifyContentCenter].join(' ')}
icon='arrow-left'
iconSize='24px'
iconClassName={[_s.mr5, _s.fillPrimary].join(' ')}
onClick={this.handleBackClick}
/>
}
<Heading size='h1'>
{title}
</Heading>
{
!!actions &&
<div className={[_s.default, _s.bgTransparent, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.mlAuto].join(' ')}>
{
actions.map((action, i) => (
<Button
isNarrow
backgroundColor='none'
color='primary'
onClick={() => action.onClick()}
key={`action-btn-${i}`}
className={[_s.ml5, _s.px5].join(' ')}
icon={action.icon}
iconClassName={_s.fillPrimary}
iconSize='14px'
/>
))
}
</div>
}
</div>
}
{

@ -89,6 +89,8 @@ class Status extends ImmutablePureComponent {
cachedMediaWidth: PropTypes.number,
contextType: PropTypes.string,
commentsLimited: PropTypes.bool,
onOpenLikes: PropTypes.func.isRequired,
onOpenReposts: PropTypes.func.isRequired,
}
// Avoid checking props that are functions (and whose equality will always
@ -472,6 +474,8 @@ class Status extends ImmutablePureComponent {
onReply={this.props.onReply}
onRepost={this.props.onRepost}
onShare={this.props.onShare}
onOpenLikes={this.props.onOpenLikes}
onOpenReposts={this.props.onOpenReposts}
/>
}

@ -37,6 +37,8 @@ class StatusActionBar extends ImmutablePureComponent {
onReply: PropTypes.func.isRequired,
onRepost: PropTypes.func.isRequired,
status: ImmutablePropTypes.map.isRequired,
onOpenLikes: PropTypes.func.isRequired,
onOpenReposts: PropTypes.func.isRequired,
}
updateOnProps = ['status']
@ -58,15 +60,11 @@ class StatusActionBar extends ImmutablePureComponent {
}
openLikesList = () => {
// : todo :
}
toggleCommentsVisible = () => {
// : todo :
this.props.onOpenLikes(this.props.status)
}
openRepostsList = () => {
// : todo :
this.props.onOpenReposts(this.props.status)
}
setRepostButton = (n) => {

@ -35,6 +35,7 @@ export default class Switch extends PureComponent {
circle: 1,
border1PX: 1,
mlAuto: 1,
bgPrimary: 1,
borderColorSecondary: 1,
bgBrand: checked,
})

@ -56,6 +56,8 @@ export const MODAL_MEDIA = 'MEDIA'
export const MODAL_MUTE = 'MUTE'
export const MODAL_PRO_UPGRADE = 'PRO_UPGRADE'
export const MODAL_REPORT = 'REPORT'
export const MODAL_STATUS_LIKES = 'STATUS_LIKES'
export const MODAL_STATUS_REPOSTS = 'STATUS_REPOSTS'
export const MODAL_STATUS_REVISIONS = 'STATUS_REVISIONS'
export const MODAL_UNAUTHORIZED = 'UNAUTHORIZED'
export const MODAL_UNFOLLOW = 'UNFOLLOW'
@ -69,13 +71,14 @@ export const FONT_SIZES_LARGE = '16px'
export const FONT_SIZES_EXTRA_LARGE = '17px'
export const FONT_SIZES = {
'extra-small': FONT_SIZES_EXTRA_SMALL,
small: FONT_SIZES_SMALL,
normal: FONT_SIZES_NORMAL,
medium: FONT_SIZES_MEDIUM,
large: FONT_SIZES_LARGE,
'extra-large': FONT_SIZES_EXTRA_LARGE,
'extra-small': '12px',
small: '13px',
normal: '14px',
medium: '15px',
large: '16px',
'extra-large': '17px',
}
export const THEMES = [
'light',
'muted',

@ -5,39 +5,55 @@ import {
DEFAULT_FONT_SIZE,
} from '../constants'
export default class Display extends PureComponent {
const mapStateToProps = (state) => ({
fontSize: state.getIn(['settings', 'displayOptions', 'fontSize']),
radiusSmallDisabled: state.getIn(['settings', 'displayOptions', 'radiusSmallDisabled']),
radiusCircleDisabled: state.getIn(['settings', 'displayOptions', 'radiusCircleDisabled']),
theme: state.getIn(['settings', 'displayOptions', 'theme']),
})
export default
@connect(mapStateToProps)
class Display extends PureComponent {
static propTypes = {
theme: PropTypes.string.isRequired,
rounded: PropTypes.bool.isRequired,
fontSize: PropTypes.string.isRequired,
radiusSmallDisabled: PropTypes.bool.isRequired,
radiusCircleDisabled: PropTypes.bool.isRequired,
theme: PropTypes.string.isRequired,
}
state = {
theme: this.props.theme,
rounded: this.props.rounded,
radiusSmallDisabled: this.props.radiusSmallDisabled,
radiusCircleDisabled: this.props.radiusCircleDisabled,
fontSize: this.props.fontSize,
}
static defaultProps = {
theme: 'BLACK',
rounded: false,
theme: 'light',
radiusSmallDisabled: true,
radiusCircleDisabled: true,
fontSize: 'normal',
}
componentDidMount() {
this.updateTheme(this.state.theme)
this.updateRounded(this.state.rounded)
this.updateRadiusSmallDisabled(this.state.radiusSmallDisabled)
this.updateRadiusCircleDisabled(this.state.radiusCircleDisabled)
this.updateFontSizes(this.state.fontSize)
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("nextProps:", nextProps)
if (nextProps.theme !== prevState.theme ||
nextProps.rounded !== prevState.rounded ||
nextProps.radiusSmallDisabled !== prevState.radiusSmallDisabled ||
nextProps.radiusCircleDisabled !== prevState.radiusCircleDisabled ||
nextProps.fontSize !== prevState.fontSize) {
return {
theme: nextProps.theme,
rounded: nextProps.rounded,
radiusSmallDisabled: nextProps.radiusSmallDisabled,
radiusCircleDisabled: nextProps.radiusCircleDisabled,
fontSize: nextProps.fontSize,
}
}
@ -50,8 +66,12 @@ export default class Display extends PureComponent {
this.updateTheme(this.state.theme)
}
if (prevState.rounded !== this.state.rounded) {
this.updateRounded(this.state.rounded)
if (prevState.radiusSmallDisabled !== this.state.radiusSmallDisabled) {
this.updateRadiusSmallDisabled(this.state.radiusSmallDisabled)
}
if (prevState.radiusCircleDisabled !== this.state.radiusCircleDisabled) {
this.updateRadiusCircleDisabled(this.state.radiusCircleDisabled)
}
if (prevState.fontSize !== this.state.fontSize) {
@ -59,16 +79,24 @@ export default class Display extends PureComponent {
}
}
updateRounded(rounded) {
if (rounded) {
document.documentElement.removeAttribute('rounded')
updateRadiusSmallDisabled(disabled) {
if (disabled) {
document.documentElement.setAttribute('no-radius', '');
} else {
document.documentElement.setAttribute('rounded', '')
document.documentElement.removeAttribute('no-radius')
}
}
updateRadiusCircleDisabled(disabled) {
if (disabled) {
document.documentElement.setAttribute('no-circle', '');
} else {
document.documentElement.removeAttribute('no-circle')
}
}
updateFontSizes(fontSize) {
let correctedFontSize = fontSize.toUpperCase()
let correctedFontSize = fontSize.toLowerCase()
if (!FONT_SIZES.hasOwnProperty(correctedFontSize)) {
correctedFontSize = DEFAULT_FONT_SIZE
}
@ -78,7 +106,7 @@ export default class Display extends PureComponent {
updateTheme(theme) {
let correctedTheme = theme.toLowerCase()
if (!THEMES.hasOwnProperty(correctedTheme)) {
if (THEMES.indexOf(correctedTheme) < 0) {
correctedTheme = DEFAULT_THEME
}

@ -56,7 +56,7 @@ const makeMapStateToProps = () => {
const ids = [status.get('id')]
const r = state.getIn(['contexts', 'replies', ids[0]])
console.log("r:", r)
// console.log("r:", r)
while (ids.length > 0) {
let id = ids.shift()
@ -253,6 +253,15 @@ const mapDispatchToProps = (dispatch) => ({
onFetchComments(statusId) {
dispatch(fetchComments(statusId))
},
onOpenLikes(status) {
dispatch(openModal('STATUS_LIKES', { status }))
},
onOpenReposts(status) {
dispatch(openModal('STATUS_REPOSTS', { status }))
},
});
export default connect(makeMapStateToProps, mapDispatchToProps)(Status);

@ -17,7 +17,7 @@ const messages = defineMessages({
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Community timeline' },
community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Community feed' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
compose: { id: 'navigation_bar.compose', defaultMessage: 'Compose new gab' },

@ -12,15 +12,9 @@ const messages = defineMessages({
reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
});
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
const mapStateToProps = (state, props) => ({
account: getAccount(state, props.id),
});
return mapStateToProps;
};
const makeMapStateToProps = () => ({
account: makeGetAccount()(state, props.id),
})
const mapDispatchToProps = (dispatch, { id }) => ({
onAuthorize() {

@ -1 +0,0 @@
export { default } from './list'

@ -1,66 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { removeFromListAdder, addToListAdder } from '../../../../actions/lists';
import Button from '../../../../components/button';
import Icon from '../../../../components/icon';
const messages = defineMessages({
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
});
const MapStateToProps = (state, { listId, added }) => ({
list: state.get('lists').get(listId),
added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added,
});
const mapDispatchToProps = (dispatch, { listId }) => ({
onRemove: () => dispatch(removeFromListAdder(listId)),
onAdd: () => dispatch(addToListAdder(listId)),
});
// : todo :
export default
@connect(MapStateToProps, mapDispatchToProps)
@injectIntl
class List extends ImmutablePureComponent {
static propTypes = {
list: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
onRemove: PropTypes.func.isRequired,
onAdd: PropTypes.func.isRequired,
added: PropTypes.bool,
};
static defaultProps = {
added: false,
};
render () {
const { list, intl, onRemove, onAdd, added } = this.props;
return (
<div className='list'>
<div className='list__wrapper'>
<div className='list__name'>
<Icon id='list-ul' className='list__name-icon' fixedWidth />
{list.get('title')}
</div>
<div className='list__btn-block'>
{
added ?
<Button icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />
:
<Button icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />
}
</div>
</div>
</div>
);
}
}

@ -1,31 +0,0 @@
.list {
padding: 4px;
border-bottom: 1px solid lighten($ui-base-color, 8%);
&__wrapper {
display: flex;
.account__relationship {
padding: 8px 5px 0 5px;
}
}
&__name {
flex: 1 1 auto;
overflow: hidden;
text-decoration: none;
font-size: 16px;
padding: 10px;
}
&__name-icon {
display: inline-block;
margin-right: 5px;
}
&__btn-block {
height: auto;
position: relative;
padding: 0 0 0 5px;
}
}

@ -1 +0,0 @@
export { default } from './list_adder'

@ -1,90 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl } from 'react-intl';
import { getOrderedLists } from '../../selectors'
import { setupListAdder, resetListAdder } from '../../actions/lists';
import List from './components/list';
import Account from '../../components/account';
import Button from '../../components/button';
// import NewListForm from '../lists_directory/components/new_list_form';
const mapStateToProps = (state, { accountId }) => ({
listIds: getOrderedLists(state).map(list => list.get('id')),
account: state.getIn(['accounts', accountId]),
});
const mapDispatchToProps = (dispatch) => ({
onInitialize: accountId => dispatch(setupListAdder(accountId)),
onReset: () => dispatch(resetListAdder()),
});
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
subheading: { id: 'lists.subheading', defaultMessage: 'Your lists' },
add: { id: 'lists.new.create', defaultMessage: 'Add List' },
headerTitle: { id: 'list_adder.header_title', defaultMessage: 'Add or Remove from Lists' },
});
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ListAdder extends ImmutablePureComponent {
static propTypes = {
accountId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
onInitialize: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired,
listIds: ImmutablePropTypes.list.isRequired,
account: ImmutablePropTypes.map.isRequired,
};
componentDidMount() {
const { onInitialize, accountId } = this.props;
onInitialize(accountId);
}
componentWillUnmount() {
this.props.onReset();
}
onClickClose = () => {
this.props.onClose('LIST_ADDER');
};
render() {
const { listIds, intl, account } = this.props;
return (
<div className='modal-root__modal compose-modal'>
<div className='compose-modal__header'>
<h3 className='compose-modal__header__title'>
{intl.formatMessage(messages.headerTitle)}
</h3>
<Button className='compose-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={this.onClickClose} size={20} />
</div>
<div className='compose-modal__content'>
<div className='list-adder'>
<div className='list-adder__account'>
<Account account={account} displayOnly/>
</div>
<br />
{ /* <NewListForm /> */ }
<br />
<div className='list-adder__lists'>
{
listIds.map(ListId => <List key={ListId} listId={ListId} />)
}
</div>
</div>
</div>
</div>
);
}
}

@ -0,0 +1,316 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import isObject from 'lodash.isobject'
import {
setupListEditor,
resetListEditor,
removeFromListEditor,
addToListEditor,
fetchListSuggestions,
clearListSuggestions,
changeListSuggestions,
} from '../actions/lists'
import { openModal } from '../actions/modal'
import {
MODAL_LIST_EDITOR,
MODAL_LIST_DELETE,
} from '../constants'
import Account from '../components/account'
import Button from '../components/button'
import Divider from '../components/divider'
import Input from '../components/input'
import TabBar from '../components/tab_bar'
import Text from '../components/text'
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
save: { id: 'lists.new.save_title', defaultMessage: 'Save Title' },
changeTitle: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
editListTitle: { id: 'lists.new.edit_title_placeholder', defaultMessage: 'Edit list title' },
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
search: { id: 'lists.search', defaultMessage: 'Search people...' },
searchMembers: { id: 'lists.search_members', defaultMessage: 'Search members...' },
searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
})
const mapStateToProps = (state, { params, id }) => {
const listId = isObject(params) ? params['id'] : id
return {
listId,
list: state.getIn(['lists', listId]),
title: state.getIn(['listEditor', 'title']),
disabled: !state.getIn(['listEditor', 'isChanged']),
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
searchAccountIds: state.getIn(['listEditor', 'suggestions', 'items']),
searchSuggestionsValue: state.getIn(['listEditor', 'suggestions', 'value']),
}
}
const mapDispatchToProps = (dispatch) => ({
onDeleteList(list) {
dispatch(openModal(MODAL_LIST_DELETE, { list }))
},
onChangeTitle(value) {
dispatch(changeListEditorTitle(value))
},
onUpdateList() {
dispatch(submitListEditor(false))
},
onInitialize(listId) {
dispatch(setupListEditor(listId))
},
onReset() {
dispatch(resetListEditor())
},
onRemoveAccountFromList(accountId) {
dispatch(removeFromListEditor(accountId))
},
onAddAccountToList(accountId) {
dispatch(addToListEditor(accountId))
},
onSubmitSearchSuggestions(value) {
dispatch(fetchListSuggestions(value))
},
onClearSearchSuggestions() {
dispatch(clearListSuggestions())
},
onChangeSuggestions(value) {
dispatch(changeListSuggestions(value))
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ListEdit extends ImmutablePureComponent {
static propTypes = {
list: ImmutablePropTypes.map,
title: PropTypes.string,
listId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
onInitialize: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired,
searchSuggestionsValue: PropTypes.string.isRequired,
accountIds: ImmutablePropTypes.list.isRequired,
searchAccountIds: ImmutablePropTypes.list.isRequired,
onRemoveAccountFromList: PropTypes.func.isRequired,
onAddAccountToList: PropTypes.func.isRequired,
onChangeSuggestions: PropTypes.func.isRequired,
onClearSearchSuggestions: PropTypes.func.isRequired,
onSubmitSearchSuggestions: PropTypes.func.isRequired,
onDeleteList: PropTypes.func.isRequired,
tab: PropTypes.string,
}
state = {
activeTab: this.props.tab || 'members'
}
componentDidMount() {
const { onInitialize, listId } = this.props
if (listId) {
onInitialize(listId)
}
}
componentDidUpdate(prevProps) {
if (this.props.listId !== prevProps.listId) {
this.props.onInitialize(this.props.listId)
}
}
componentWillUnmount() {
this.props.onReset()
}
handleChangeTab = (tab) => {
this.setState({ activeTab: tab })
}
onClickClose = () => {
this.props.onClose(MODAL_LIST_EDITOR)
}
handleOnDeleteList = () => {
this.props.onDeleteList(this.props.list)
}
handleAddOrRemoveFromList = (accountId) => {
if (this.props.accountIds.includes(accountId)) {
this.props.onRemoveAccountFromList(accountId)
} else {
this.props.onAddAccountToList(accountId)
}
}
handleSearchSuggestionsChange = (value) => {
this.props.onChangeSuggestions(value)
}
handleSearchSuggestionsKeyUp = (e) => {
if (e.keyCode === 13) {
this.props.onSubmitSearchSuggestions(this.props.searchSuggestionsValue)
}
}
handleSearchSuggestionsSubmit = () => {
this.props.onSubmitSearchSuggestions(this.props.searchSuggestionsValue)
}
render() {
const {
title,
accountIds,
searchAccountIds,
intl,
searchSuggestionsValue,
} = this.props
const { activeTab } = this.state
// : todo : save new list title
return (
<div>
<div className={[_s.default, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}>
<div className={[_s.default, _s.z4, _s.bgPrimary, _s.px15, _s.top0, _s.posSticky, _s.borderBottom1PX, _s.borderColorSecondary,].join(' ')}>
<TabBar
tabs={[
{
title: 'Members list',
onClick: () => this.handleChangeTab('members'),
active: activeTab === 'members',
},
{
title: 'Add new',
onClick: () => this.handleChangeTab('add-new'),
active: activeTab === 'add-new',
},
{
title: 'Settings',
onClick: () => this.handleChangeTab('settings'),
active: activeTab === 'settings',
},
]}
/>
</div>
{
activeTab === 'members' &&
<div className={[_s.default, _s.mb10, _s.py10].join(' ')}>
<div className={[_s.default, _s.pb10, _s.pt5].join(' ')}>
<div className={[_s.default].join(' ')}>
<Text weight='bold' size='small' color='secondary' className={[_s.default, _s.px15, _s.mt5, _s.mb15].join(' ')}>
Total members ({accountIds.size})
</Text>
{
accountIds &&
accountIds.map((accountId) => (
<Account
compact
key={`remove-from-list-${accountId}`}
id={accountId}
onActionClick={() => this.handleAddOrRemoveFromList(accountId)}
actionIcon={'subtract'}
/>
))
}
</div>
</div>
</div>
}
{
activeTab === 'settings' &&
<div className={[_s.default, _s.mb10, _s.pb10, _s.px15].join(' ')}>
<div className={[_s.default, _s.py15].join(' ')}>
<Input
title={intl.formatMessage(messages.editListTitle)}
placeholder='My new list title...'
value={title}
// onChange={onChange}
// onSubmit={onSubmit}
// disabled={disabled}
/>
</div>
<Divider />
<div className={_s.mb10}>
<Button
onClick={this.handleOnDeleteList}
backgroundColor='danger'
>
Delete List
</Button>
</div>
<Text size='extraSmall' color='secondary'>
Once you delete a list you cannot retrieve it.
</Text>
</div>
}
{
activeTab === 'add-new' &&
<div className={[_s.default, _s.mb10, _s.py10].join(' ')}>
<div className={[_s.default, _s.px15].join(' ')}>
<Input
placeholder={intl.formatMessage(messages.search)}
value={searchSuggestionsValue}
onChange={this.handleSearchSuggestionsChange}
onKeyUp={this.handleSearchSuggestionsKeyUp}
handleSubmit={this.handleSearchSuggestionsSubmit}
title={intl.formatMessage(messages.searchTitle)}
prependIcon='search'
hideLabel
/>
</div>
<div className={[_s.default, _s.pt10].join(' ')}>
<div className={[_s.default].join(' ')}>
<Text weight='bold' size='small' color='secondary' className={[_s.default, _s.px15, _s.mt5, _s.mb15].join(' ')}>
Search results ({searchAccountIds.size})
</Text>
{
searchAccountIds &&
searchAccountIds.map((accountId) => {
if (accountIds.includes(accountId)) return null
return (
<Account
key={`add-to-list-${accountId}`}
id={accountId}
compact
onActionClick={() => this.handleAddOrRemoveFromList(accountId)}
actionIcon='add'
/>
)
})
}
</div>
</div>
</div>
}
</div>
</div>
)
}
}

@ -1,78 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { removeFromListEditor, addToListEditor } from '../../../actions/lists';
import { makeGetAccount } from '../../../selectors';
import Avatar from '../../../components/avatar';
import DisplayName from '../../../components/display_name';
import Button from '../../../components/button';
const messages = defineMessages({
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
});
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
const mapStateToProps = (state, { accountId, added }) => ({
account: getAccount(state, accountId),
added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added,
});
return mapStateToProps;
};
const mapDispatchToProps = (dispatch, { accountId }) => ({
onRemove: () => dispatch(removeFromListEditor(accountId)),
onAdd: () => dispatch(addToListEditor(accountId)),
});
export default
@connect(makeMapStateToProps, mapDispatchToProps)
@injectIntl
class Account extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
onRemove: PropTypes.func.isRequired,
onAdd: PropTypes.func.isRequired,
added: PropTypes.bool,
};
static defaultProps = {
added: false,
};
render () {
const { account, intl, onRemove, onAdd, added } = this.props;
let button;
if (added) {
button = <Button icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
} else {
button = <Button icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
}
return (
<div className='account'>
<div className='account__wrapper'>
<div className='account__display-name'>
<div className='account__avatar-wrapper'>
<Avatar account={account} size={36} />
</div>
<DisplayName account={account} />
</div>
<div className='account__relationship'>
{button}
</div>
</div>
</div>
);
}
}

@ -1,49 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import { changeListEditorTitle, submitListEditor } from '../../../../actions/lists';
const messages = defineMessages({
title: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
save: { id: 'lists.new.save_title', defaultMessage: 'Save Title' },
});
const mapStateToProps = (state) => ({
value: state.getIn(['listEditor', 'title']),
disabled: !state.getIn(['listEditor', 'isChanged']),
});
const mapDispatchToProps = (dispatch) => ({
onChange: value => dispatch(changeListEditorTitle(value)),
onSubmit: () => dispatch(submitListEditor(false)),
});
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ListForm extends PureComponent {
static propTypes = {
value: PropTypes.string.isRequired,
disabled: PropTypes.bool,
intl: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};
render () {
const { value, disabled, intl, onSubmit, onChange } = this.props;
return null;
// return (
// <ColumnInlineForm
// value={value}
// onChange={onChange}
// onSubmit={onSubmit}
// label={intl.formatMessage(messages.title)}
// btnTitle={intl.formatMessage(messages.save)}
// disabled={disabled}
// />
// );
}
}

@ -1 +0,0 @@
export { default } from './edit_list_form'

@ -1 +0,0 @@
export { default } from './list_editor_search'

@ -1,68 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../../actions/lists';
import Search from '../../../../components/search';
const messages = defineMessages({
search: { id: 'lists.search', defaultMessage: 'Search among people you follow' },
searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
});
const mapStateToProps = (state) => ({
value: state.getIn(['listEditor', 'suggestions', 'value']),
});
const mapDispatchToProps = (dispatch) => ({
onSubmit: (value) => dispatch(fetchListSuggestions(value)),
onClear: () => dispatch(clearListSuggestions()),
onChange: (value) => dispatch(changeListSuggestions(value)),
});
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ListEditorSearch extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onClear: PropTypes.func.isRequired,
};
handleChange = (value) => {
this.props.onChange(value);
}
handleKeyUp = e => {
if (e.keyCode === 13) {
this.props.onSubmit(this.props.value);
}
}
handleSubmit = () => {
this.props.onSubmit(this.props.value);
}
handleClear = () => {
this.props.onClear();
}
render () {
const { value, intl } = this.props;
return (
<Search
className='list-editor-search'
placeholder={intl.formatMessage(messages.search)}
value={value}
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}
handleSubmit={this.handleSubmit}
handleClear={this.handleClear}
searchTitle={intl.formatMessage(messages.searchTitle)}
/>
)
}
}

@ -1 +0,0 @@
export { default } from './list_edit'

@ -1,127 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import isObject from 'lodash.isobject'
import { setupListEditor, resetListEditor } from '../../actions/lists'
import Account from './components/account'
import ListEditorSearch from './components/list_editor_search'
import EditListForm from './components/edit_list_form/edit_list_form'
import Button from '../../components/button'
import Form from '../../components/form'
import Input from '../../components/input'
const mapStateToProps = (state, { params }) => {
console.log("params:", params)
const listId = isObject(params) ? params['id'] : null
return {
listId,
title: state.getIn(['listEditor', 'title']),
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
searchAccountIds: state.getIn(['listEditor', 'suggestions', 'items']),
}
}
const mapDispatchToProps = (dispatch) => ({
onInitialize: (listId) => dispatch(setupListEditor(listId)),
onReset: () => dispatch(resetListEditor()),
})
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
changeTitle: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
editListTitle: { id: 'lists.new.edit_title_placeholder', defaultMessage: 'Edit list title' },
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ListEdit extends ImmutablePureComponent {
static propTypes = {
title: PropTypes.string,
listId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
onInitialize: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.list.isRequired,
searchAccountIds: ImmutablePropTypes.list.isRequired,
}
componentDidMount() {
const { onInitialize, listId } = this.props
console.log("listId:", listId)
if (listId) {
onInitialize(listId)
}
}
componentWillUnmount() {
this.props.onReset()
}
onClickClose = () => {
this.props.onClose('LIST_ADDER')
}
render() {
const {
title,
accountIds,
searchAccountIds,
intl
} = this.props
console.log("title:", title)
return (
<Form>
<Input
title={intl.formatMessage(messages.editListTitle)}
placeholder='My new list title...'
value={title}
// onChange={onChange}
// onSubmit={onSubmit}
// disabled={disabled}
/>
<div className='compose-modal__header'>
<h3 className='compose-modal__header__title'>
{intl.formatMessage(messages.editList)}
</h3>
<Button className='compose-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={this.onClickClose} size={20} />
</div>
<div className='compose-modal__content'>
<div className='list-editor'>
<EditListForm />
<br />
{
accountIds.size > 0 &&
<div>
<div className='list-editor__accounts'>
{accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
</div>
</div>
}
<br />
<ListEditorSearch />
<div className='list-editor__accounts'>
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
</div>
</div>
</div>
</Form>
)
}
}

@ -71,7 +71,8 @@ class ListTimeline extends ImmutablePureComponent {
}
handleEditClick = () => {
this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id }))
console.log("handleEditClick:", this.props.params.id)
this.props.dispatch(openModal('LIST_EDITOR', { id: this.props.params.id }))
}
render() {
@ -86,20 +87,22 @@ class ListTimeline extends ImmutablePureComponent {
}
const emptyMessage = (
<div className={[_s.default, _s.py15, _s.px15].join(' ')}>
<div className={[_s.default, _s.py15, _s.px15, _s.alignItemsCenter].join(' ')}>
<FormattedMessage
id='empty_column.list'
defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.'
/>
<Button
onClick={this.handleEditClick}
className={[_s.mt10]}
>
<Text color='inherit' align='center'>
<FormattedMessage id='list.click_to_add' defaultMessage='Click here to add people' />
</Text>
</Button>
<div className={_s.mt10}>
<Button
onClick={this.handleEditClick}
className={[_s.mt10]}
>
<Text color='inherit' align='center'>
<FormattedMessage id='list.click_to_add' defaultMessage='Click here to add people' />
</Text>
</Button>
</div>
</div>
)

@ -1,7 +1,50 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { fetchStatus } from '../actions/statuses'
import StatusContainer from '../containers/status_container'
import ColumnIndicator from '../components/column_indicator'
const mapStateToProps = (state, props) => {
const statusId = props.id || props.params.statusId
return {
status: state.getIn(['statuses', statusId]),
}
}
const mapDispatchToProps = (dispatch) => ({
onFetchStatus: (id) => dispatch(fetchStatus(id)),
})
export default
@connect(mapStateToProps, mapDispatchToProps)
class Status extends ImmutablePureComponent {
static propTypes = {
onFetchStatus: PropTypes.func.isRequired,
params: PropTypes.object,
status: ImmutablePropTypes.map,
}
updateOnProps = [
'params',
'status',
]
componentDidMount() {
const statusId = this.props.id || this.props.params.statusId
this.props.onFetchStatus(statusId)
}
export default class Status extends PureComponent {
render() {
const { status } = this.props
// - if comment render as such
if (!status) {
return <ColumnIndicator type='loading' />
}
return (
<StatusContainer {...this.props} />
)

@ -1,72 +1,70 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ColumnIndicator from '../components/column_indicator'
import { FormattedMessage } from 'react-intl'
import { fetchLikes } from '../actions/interactions'
import { fetchStatus } from '../actions/statuses'
import { makeGetStatus } from '../selectors'
import Account from '../components/account'
import ColumnIndicator from '../components/column_indicator'
import ScrollableList from '../components/scrollable_list'
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },
});
const mapStateToProps = (state, props) => {
const getStatus = makeGetStatus()
const status = getStatus(state, {
id: props.params.statusId,
username: props.params.username,
})
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
});
return {
status,
accountIds: state.getIn(['user_lists', 'liked_by', props.params.statusId]),
}
}
export default
@injectIntl
@connect(mapStateToProps)
class StatusLikes extends ImmutablePureComponent {
class StatusReposts extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
status: ImmutablePropTypes.map,
}
componentWillMount () {
if (!this.props.accountIds) {
this.props.dispatch(fetchLikes(this.props.params.statusId));
}
this.props.dispatch(fetchLikes(this.props.params.statusId))
this.props.dispatch(fetchStatus(this.props.params.statusId))
}
componentWillReceiveProps (nextProps) {
componentWillReceiveProps(nextProps) {
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
this.props.dispatch(fetchLikes(nextProps.params.statusId));
this.props.dispatch(fetchLikes(nextProps.params.statusId))
this.props.dispatch(fetchStatus(nextProps.params.statusId))
}
}
handleRefresh = () => {
this.props.dispatch(fetchLikes(this.props.params.statusId));
}
render () {
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
const { accountIds, status } = this.props
if (!accountIds) {
return <ColumnIndicator type='loading' />
} else if (!status) {
return <ColumnIndicator type='missing' />
}
const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favourited this toot yet. When someone does, they will show up here.' />;
return (
<div>
<ScrollableList
scrollKey='favourites'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>
{accountIds.map(id =>
<Account key={id} id={id} withNote={false} />,
)}
</ScrollableList>
</div>
);
<ScrollableList
scrollKey='likes'
emptyMessage={<FormattedMessage id='status.likes.empty' defaultMessage='No one has liked this gab yet. When someone does, they will show up here.' />}
>
{
accountIds.map(id =>
<Account key={id} id={id} />
)
}
</ScrollableList>
)
}
}
}

@ -145,7 +145,7 @@ class SwitchingArea extends PureComponent {
<Redirect from='/' to='/home' exact />
<WrappedRoute path='/home' exact page={HomePage} component={HomeTimeline} content={children} />
<WrappedRoute path='/timeline/all' exact page={CommunityPage} component={CommunityTimeline} content={children} componentParams={{ title: 'Community Timeline' }} />
<WrappedRoute path='/timeline/all' exact page={CommunityPage} component={CommunityTimeline} content={children} componentParams={{ title: 'Community Feed' }} />
<WrappedRoute path='/groups' exact page={GroupsPage} component={GroupsCollection} content={children} componentParams={{ activeTab: 'featured' }} />
<WrappedRoute path='/groups/new' exact page={GroupsPage} component={GroupsCollection} content={children} componentParams={{ activeTab: 'new' }} />

@ -65,6 +65,8 @@ export function SidebarMorePopover() { return import(/* webpackChunkName: "compo
export function StatusLikes() { return import(/* webpackChunkName: "features/status_likes" */'../../status_likes') }
export function StatusOptionsPopover() { return import(/* webpackChunkName: "components/status_options_popover" */'../../../components/popover/status_options_popover') }
export function StatusReposts() { return import(/* webpackChunkName: "features/status_reposts" */'../../status_reposts') }
export function StatusLikesModal() { return import(/* webpackChunkName: "modals/status_likes_modal" */'../../../components/modal/status_likes_modal') }
export function StatusRepostsModal() { return import(/* webpackChunkName: "modals/status_reposts_modal" */'../../../components/modal/status_reposts_modal') }
export function StatusRevisionsModal() { return import(/* webpackChunkName: "modals/status_revisions_modal" */'../../../components/modal/status_revisions_modal') }
export function StatusSharePopover() { return import(/* webpackChunkName: "components/status_share_popover" */'../../../components/popover/status_share_popover') }
export function StatusVisibilityPopover() { return import(/* webpackChunkName: "components/status_visibility_popover" */'../../../components/popover/status_visibility_popover') }

@ -1,32 +1,33 @@
import Layout from './layout'
export default class DefaultLayout extends PureComponent {
static propTypes = {
actions: PropTypes.array,
tabs: PropTypes.array,
children: PropTypes.node.isRequired,
layout: PropTypes.object,
showBackBtn: PropTypes.bool,
tabs: PropTypes.array,
title: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
}
render() {
const {
children,
title,
showBackBtn,
layout,
actions,
children,
layout,
showBackBtn,
tabs,
} = this.props
title,
} = this.props
return (
<Layout
title={title}
showBackBtn={showBackBtn}
actions={actions}
tabs={tabs}
layout={layout}
showBackBtn={showBackBtn}
tabs={tabs}
title={title}
>
{children}
</Layout>

@ -5,32 +5,34 @@ import Layout from './layout'
import GroupHeader from '../components/group_header'
export default class GroupLayout extends ImmutablePureComponent {
static propTypes = {
actions: PropTypes.array,
children: PropTypes.node,
group: ImmutablePropTypes.map,
relationships: ImmutablePropTypes.map,
layout: PropTypes.object,
relationships: ImmutablePropTypes.map,
showBackBtn: PropTypes.bool,
title: PropTypes.string,
}
render() {
const {
group,
children,
layout,
showBackBtn,
actions,
children,
group,
layout,
relationships,
showBackBtn,
title,
} = this.props
const title = !!group ? group.get('title') : undefined
return (
<Layout
noRightSidebar
title={title}
actions={actions}
showBackBtn={showBackBtn}
title={title}
>
<div className={[_s.default, _s.width100PC, _s.pl15].join(' ')}>

@ -1,52 +1,58 @@
import Sticky from 'react-stickynode'
import Search from '../components/search'
import ColumnHeader from '../components/column_header'
import Sidebar from '../components/sidebar'
import { BREAKPOINT_EXTRA_SMALL } from '../constants'
import NavigationBar from '../components/navigation_bar'
// : todo :
// import Footer from '../components/footer'
import FloatingActionButton from '../components/floating_action_button'
import Responsive from '../features/ui/util/responsive_component'
export default class Layout extends PureComponent {
static propTypes = {
actions: PropTypes.array,
tabs: PropTypes.array,
children: PropTypes.node,
layout: PropTypes.object,
title: PropTypes.string,
showBackBtn: PropTypes.bool,
noSidebar: PropTypes.bool,
noRightSidebar: PropTypes.bool,
noComposeButton: PropTypes.bool,
noRightSidebar: PropTypes.bool,
noSidebar: PropTypes.bool,
showBackBtn: PropTypes.bool,
tabs: PropTypes.array,
title: PropTypes.string,
}
render() {
const {
children,
title,
showBackBtn,
layout,
actions,
tabs,
noSidebar,
noRightSidebar,
children,
layout,
noComposeButton,
noRightSidebar,
noSidebar,
showBackBtn,
tabs,
title,
} = this.props
return (
<div className={[_s.default, _s.width100PC, _s.heightMin100VH, _s.bgTertiary].join(' ')}>
<NavigationBar
title={title}
tabs={tabs}
actions={actions}
tabs={tabs}
title={title}
/>
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
{
!noSidebar &&
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
<Sidebar title={title} tabs={tabs} actions={actions} />
<Sidebar
actions={actions}
showBackBtn={showBackBtn}
tabs={tabs}
title={title}
/>
</Responsive>
}

@ -1,17 +1,16 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import Sticky from 'react-stickynode'
import ProfileHeader from '../components/profile_header'
import NavigationBar from '../components/navigation_bar'
import ProfileHeader from '../components/profile_header'
export default class ProfileLayout extends ImmutablePureComponent {
static propTypes = {
children: PropTypes.node.isRequired,
account: ImmutablePropTypes.map,
children: PropTypes.node.isRequired,
layout: PropTypes.object,
title: PropTypes.string,
showBackBtn: PropTypes.bool,
}
render() {
@ -20,7 +19,6 @@ export default class ProfileLayout extends ImmutablePureComponent {
children,
layout,
title,
showBackBtn,
} = this.props
return (

@ -45,7 +45,7 @@
"bundle_modal_error.message": "Something went wrong while loading this component.",
"bundle_modal_error.retry": "Try again",
"column.blocks": "Blocked users",
"column.community": "Community timeline",
"column.community": "Community feed",
"column.direct": "Direct messages",
"column.domain_blocks": "Blocked domains",
"column.favorites": "Favorites",
@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Зареждане...",
"media_gallery.toggle_visible": "Hide media",
@ -228,7 +228,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -1688,7 +1688,7 @@
{
"descriptors": [
{
"defaultMessage": "Search among people you follow",
"defaultMessage": "Search people...",
"id": "lists.search"
}
],

@ -45,7 +45,7 @@
"bundle_modal_error.message": "Something went wrong while loading this component.",
"bundle_modal_error.retry": "Try again",
"column.blocks": "Blocked users",
"column.community": "Community timeline",
"column.community": "Community feed",
"column.direct": "Direct messages",
"column.domain_blocks": "Blocked domains",
"column.favorites": "Favorites",
@ -221,7 +221,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Hide media",
@ -230,7 +230,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new gab",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "טוען...",
"media_gallery.toggle_visible": "נראה\\בלתי נראה",

@ -45,7 +45,7 @@
"bundle_modal_error.message": "Something went wrong while loading this component.",
"bundle_modal_error.retry": "Try again",
"column.blocks": "Blocked users",
"column.community": "Community timeline",
"column.community": "Community feed",
"column.direct": "Direct messages",
"column.domain_blocks": "Blocked domains",
"column.favorites": "Favorites",
@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Hide media",
@ -228,7 +228,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Učitavam...",
"media_gallery.toggle_visible": "Preklopi vidljivost",

@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Tunggu sebentar...",
"media_gallery.toggle_visible": "Tampil/Sembunyikan",

@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Kargante...",
"media_gallery.toggle_visible": "Chanjar videbleso",

@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Hide media",
@ -228,7 +228,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -45,7 +45,7 @@
"bundle_modal_error.message": "Something went wrong while loading this component.",
"bundle_modal_error.retry": "Try again",
"column.blocks": "Blocked users",
"column.community": "Community timeline",
"column.community": "Community feed",
"column.direct": "Direct messages",
"column.domain_blocks": "Blocked domains",
"column.favorites": "Favorites",
@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Hide media",
@ -228,7 +228,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Hide media",
@ -228,7 +228,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -45,7 +45,7 @@
"bundle_modal_error.message": "Something went wrong while loading this component.",
"bundle_modal_error.retry": "Try again",
"column.blocks": "Blocked users",
"column.community": "Community timeline",
"column.community": "Community feed",
"column.direct": "Direct messages",
"column.domain_blocks": "Blocked domains",
"column.favorites": "Favorites",
@ -219,7 +219,7 @@
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.search": "Search people...",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Hide media",
@ -228,7 +228,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Community timeline",
"navigation_bar.community_timeline": "Community feed",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",

@ -1,11 +1,12 @@
import { Fragment } from 'react'
import PageTitle from '../features/ui/util/page_title'
import LinkFooter from '../components/link_footer'
import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
import TrendsPanel from '../components/panel/trends_panel'
import DefaultLayout from '../layouts/default_layout'
import LinkFooter from '../components/link_footer'
import TrendsPanel from '../components/panel/trends_panel'
import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
export default class BasicPage extends PureComponent {
static propTypes = {
title: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
@ -31,4 +32,5 @@ export default class BasicPage extends PureComponent {
</DefaultLayout>
)
}
}

@ -12,7 +12,7 @@ import TimelineComposeBlock from '../components/timeline_compose_block'
import Divider from '../components/divider'
const messages = defineMessages({
community: { 'id': 'column.community', 'defaultMessage': 'Community timeline' },
community: { 'id': 'column.community', 'defaultMessage': 'Community feed' },
})
const mapDispatchToProps = (dispatch) => ({

@ -50,36 +50,12 @@ class GroupPage extends ImmutablePureComponent {
relationships,
} = this.props
// <div className="column-header__wrapper">
// <h1 className="column-header">
// <Link to={`/groups/${id}`} className={classNames('btn grouped active')}>
// {intl.formatMessage(messages.tabLatest)}
// </Link>
// <div className='column-header__buttons'>
// <button
// className={classNames('column-header__button', { 'active': !collapsed })}
// title={intl.formatMessage(collapsed ? messages.show : messages.hide)}
// aria-label={intl.formatMessage(collapsed ? messages.show : messages.hide)}
// aria-pressed={collapsed ? 'false' : 'true'}
// onClick={this.handleToggleClick}
// ><Icon id='sliders' /></button>
// </div>
// </h1>
// {!collapsed && <div className='column-header__collapsible'>
// <div className='column-header__collapsible-inner'>
// <div className='column-header__collapsible__extra'>
// <ColumnSettingsContainer />
// </div>
// </div>
// </div>}
// </div>
const groupTitle = !!group ? group.get('title') : ''
return (
<GroupLayout
showBackBtn
title={intl.formatMessage(messages.group)}
group={group}
relationships={relationships}
actions={[

@ -1,6 +1,7 @@
import { Fragment } from 'react'
import { openModal } from '../actions/modal'
import { defineMessages, injectIntl } from 'react-intl'
import { MODAL_HOME_TIMELINE_SETTINGS } from '../constants'
import IntersectionObserverArticle from '../components/intersection_observer_article'
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'
import PageTitle from '../features/ui/util/page_title'
@ -25,7 +26,7 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = (dispatch) => ({
onOpenHomePageSettingsModal() {
dispatch(openModal('HOME_TIMELINE_SETTINGS'))
dispatch(openModal(MODAL_HOME_TIMELINE_SETTINGS))
},
})
@ -35,10 +36,10 @@ export default
class HomePage extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
totalQueuedItemsCount: PropTypes.number.isRequired,
intl: PropTypes.object.isRequired,
onOpenHomePageSettingsModal: PropTypes.func.isRequired,
totalQueuedItemsCount: PropTypes.number.isRequired,
}
intersectionObserverWrapper = new IntersectionObserverWrapper()
@ -59,7 +60,6 @@ class HomePage extends PureComponent {
this.intersectionObserverWrapper.disconnect()
}
render() {
const {
intl,
@ -110,13 +110,18 @@ class HomePage extends PureComponent {
</Fragment>
)}
>
<PageTitle
path={intl.formatMessage(messages.home)}
badge={totalQueuedItemsCount}
/>
<TimelineComposeBlock autoFocus={false} />
<Divider />
{children}
</DefaultLayout>
)
}

@ -26,7 +26,7 @@ const mapDispatchToProps = (dispatch) => ({
onOpenListEditModal(list) {
if (!list) return
const listId = list.get('id')
dispatch(openModal(MODAL_LIST_EDITOR, { listId }))
dispatch(openModal(MODAL_LIST_EDITOR, { id: listId }))
},
// : todo :
// onOpenListTimelineSettingsModal() {
@ -65,7 +65,7 @@ class ListPage extends ImmutablePureComponent {
return (
<DefaultLayout
showBackBtn
title={title}
title={intl.formatMessage(messages.list)}
actions={[
{
icon: 'cog',

@ -1,21 +1,21 @@
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings';
import { STORE_HYDRATE } from '../actions/store';
import { EMOJI_USE } from '../actions/emojis';
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists';
import { Map as ImmutableMap, fromJS } from 'immutable';
import uuid from '../utils/uuid';
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings'
import { STORE_HYDRATE } from '../actions/store'
import { EMOJI_USE } from '../actions/emojis'
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists'
import { Map as ImmutableMap, fromJS } from 'immutable'
import uuid from '../utils/uuid'
const initialState = ImmutableMap({
saved: true,
onboarded: false,
skinTone: 1,
// displayOptions: ImmutableMap({
// fontSize: 'normal',
// radiusSmallEnabled: true,
// radiusCircleEnabled: true,
// theme: 'light',
// }),
displayOptions: ImmutableMap({
fontSize: 'normal',
radiusSmallDisabled: false,
radiusCircleDisabled: false,
theme: 'light',
}),
home: ImmutableMap({
shows: ImmutableMap({
@ -27,26 +27,6 @@ const initialState = ImmutableMap({
}),
}),
lists: ImmutableMap({
shows: ImmutableMap({
photos: true,
polls: true,
reply: true,
repost: true,
videos: true,
}),
}),
groups: ImmutableMap({
shows: ImmutableMap({
photos: true,
polls: true,
reply: true,
repost: true,
videos: true,
}),
}),
community: ImmutableMap({
shows: ImmutableMap({
inSidebar: false,

@ -23,6 +23,7 @@ import {
} from '../actions/accounts'
import {
REPOSTS_FETCH_SUCCESS,
LIKES_FETCH_SUCCESS,
} from '../actions/interactions'
import {
BLOCKS_FETCH_REQUEST,
@ -52,7 +53,7 @@ const initialState = ImmutableMap({
followers: ImmutableMap(),
following: ImmutableMap(),
reblogged_by: ImmutableMap(),
favorited_by: ImmutableMap(),
liked_by: ImmutableMap(),
follow_requests: ImmutableMap(),
blocks: ImmutableMap(),
mutes: ImmutableMap(),
@ -103,6 +104,8 @@ export default function userLists(state = initialState, action) {
return state.setIn(['following', action.id, 'isLoading'], false);
case REPOSTS_FETCH_SUCCESS:
return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
case LIKES_FETCH_SUCCESS:
return state.setIn(['liked_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
case FOLLOW_REQUESTS_FETCH_SUCCESS:
return state.setIn(['follow_requests', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next).setIn(['follow_requests', 'isLoading'], false);
case FOLLOW_REQUESTS_EXPAND_SUCCESS:

@ -12,6 +12,9 @@
--color_red: #de2960;
--color_red-dark: #c72c5b;
--radius-small: 8px;
--radius-circle: 9999px;
--fs_xs: 0.8571428571rem;
--fs_s: 0.9285714286rem;
--fs_n: 1rem;
@ -19,19 +22,8 @@
--fs_l: 1.1428571429rem;
--fs_xl: 1.3571428571rem;
--fs_xxl: 1.7142857143rem;
}
:root {
--radius-small: 0;
--radius-circle: 0;
}
:root[rounded] {
--radius-small: 8px;
--radius-circle: 9999px;
}
:root[theme='light'] {
/* Default light theme */
--solid_color_primary: #fff;
--solid_color_primary-opaque:rgba(255, 255, 255,.6);
--solid_color_secondary: #e2e8ec;
@ -46,34 +38,42 @@
--border_color_secondary: #ececed;
}
:root[no-circle] {
--radius-circle: 0 !important;
}
:root[no-radius] {
--radius-small: 0 !important;
}
:root[theme='muted'] {
--solid_color_primary: #222;
--solid_color_primary-opaque:rgba(34, 34, 34, .6);
--solid_color_secondary: #4f5050;
--solid_color_secondary-dark: #424343;
--solid_color_tertiary: #333;
--solid_color_block: #2d2d2d;
--solid_color_primary: #222 !important;
--solid_color_primary-opaque:rgba(34, 34, 34, .6) !important;
--solid_color_secondary: #4f5050 !important;
--solid_color_secondary-dark: #424343 !important;
--solid_color_tertiary: #333 !important;
--solid_color_block: #2d2d2d !important;
--text_color_primary: #fff;
--text_color_secondary: #7b7b7b;
--text_color_tertiary: #656565;
--text_color_primary: #fff !important;
--text_color_secondary: #7b7b7b !important;
--text_color_tertiary: #656565 !important;
--border_color_secondary: #424141;
--border_color_secondary: #424141 !important;
}
:root[theme='black'] {
--solid_color_primary: #13171b;
--solid_color_primary-opaque:rgba(19, 23, 27, .6);
--solid_color_secondary: #4f5050;
--solid_color_secondary-dark: #424343;
--solid_color_tertiary: #000;
--solid_color_block: #202327;
--solid_color_primary: #13171b !important;
--solid_color_primary-opaque:rgba(19, 23, 27, .6) !important;
--solid_color_secondary: #4f5050 !important;
--solid_color_secondary-dark: #424343 !important;
--solid_color_tertiary: #000 !important;
--solid_color_block: #202327 !important;
--text_color_primary: #cccbcb;
--text_color_secondary: #61686E;
--text_color_tertiary: #656565;
--text_color_primary: #cccbcb !important;
--text_color_secondary: #61686E !important;
--text_color_tertiary: #656565 !important;
--border_color_secondary: #212020;
--border_color_secondary: #212020 !important;
}
html,
@ -199,7 +199,6 @@ body {
.inherit {
color: inherit;
font: inherit;
white-space: inherit;
}
@ -246,6 +245,9 @@ body {
.whiteSpaceNoWrap { white-space: nowrap; }
.outlineNone { outline: none; }
.outlineOnFocus:focus {
box-shadow: 0 0 0 5px rgba(21, 156, 228, 0.4);
}
.resizeNone { resize: none; }
@ -264,6 +266,7 @@ body {
.borderRight1PX { border-right-width: 1px; }
.borderBottom1PX { border-bottom-width: 1px; }
.borderBottom6PX { border-bottom-width: 6px; }
.borderLeft1PX { border-left-width: 1px; }
.borderTop1PX { border-top-width: 1px; }
@ -393,6 +396,7 @@ body {
/* */
.posSticky { position: sticky; }
.posFixed { position: fixed; }
.posAbs { position: absolute; }
@ -699,7 +703,7 @@ body {
}
.boxShadowProfileAvatar {
box-shadow: 0 0 0 6px #F6F6F9;
box-shadow: 0 0 0 6px var(--solid_color_primary);
}
.listStyleNone {

@ -26,7 +26,7 @@ class ProofProvider::Keybase::ConfigSerializer < ActiveModel::Serializer
end
def brand_color
'#282c37'
'#21D07B'
end
def description

@ -6,7 +6,7 @@
# id :bigint(8) not null, primary key
# list_id :bigint(8) not null
# account_id :bigint(8) not null
# follow_id :bigint(8) not null
# follow_id :bigint(8) default(1)
#
class ListAccount < ApplicationRecord
@ -21,6 +21,6 @@ class ListAccount < ApplicationRecord
private
def set_follow
self.follow = Follow.find_by!(account_id: list.account_id, target_account_id: account.id) unless list.account_id == account.id
self.follow = Follow.find_by!(account_id: list.account_id, target_account_id: account.id) unless true
end
end

@ -32,7 +32,7 @@ class ManifestSerializer < ActiveModel::Serializer
end
def theme_color
'#282c37'
'#21D07B'
end
def background_color

@ -15,7 +15,7 @@
%link{ rel: 'mask-icon', href: '/mask-icon.svg', color: '#2B90D9' }/
%link{ rel: 'manifest', href: '/manifest.json' }/
%meta{ name: 'msapplication-config', content: '/browserconfig.xml' }/
%meta{ name: 'theme-color', content: '#282c37' }/
%meta{ name: 'theme-color', content: '#21D07B' }/
%meta{ name: 'apple-mobile-web-app-capable', content: 'yes' }/
- if @tag

@ -11,7 +11,6 @@
%link{ rel: 'dns-prefetch', href: storage_host }/
= stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'

@ -6,7 +6,6 @@
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
= stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'error', integrity: true, crossorigin: 'anonymous'
%body.error

@ -400,7 +400,7 @@ ActiveRecord::Schema.define(version: 2020_04_30_154012) do
create_table "list_accounts", force: :cascade do |t|
t.bigint "list_id", null: false
t.bigint "account_id", null: false
t.bigint "follow_id", null: false
t.bigint "follow_id", default: nil
t.index ["account_id", "list_id"], name: "index_list_accounts_on_account_id_and_list_id", unique: true
t.index ["follow_id"], name: "index_list_accounts_on_follow_id"
t.index ["list_id", "account_id"], name: "index_list_accounts_on_list_id_and_account_id"

@ -3,7 +3,7 @@
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#282c37</TileColor>
<TileColor>#21D07B</TileColor>
</tile>
</msapplication>
</browserconfig>