Progress on group account search

Added group member search, group removed account, album add styles
This commit is contained in:
mgabdev 2020-12-20 19:28:32 -05:00
parent 67eb9d5890
commit 1a8ecc672c
20 changed files with 225 additions and 94 deletions

View File

@ -52,7 +52,7 @@ class Api::V1::Groups::AccountsController < Api::BaseController
render json: @group, serializer: REST::GroupRelationshipSerializer, relationships: relationships render json: @group, serializer: REST::GroupRelationshipSerializer, relationships: relationships
end end
private private
def relationships def relationships

View File

@ -34,6 +34,10 @@ class Api::V1::Groups::RemovedAccountsController < Api::BaseController
render_empty_success render_empty_success
end end
def search
# : todo :
end
private private
def set_group def set_group

View File

@ -113,6 +113,16 @@ class Api::V1::GroupsController < Api::BaseController
render_empty_success render_empty_success
end end
def member_search
@accounts = Group.search_for_members(@group, params[:q], DEFAULT_ACCOUNTS_LIMIT)
render json: @accounts, each_serializer: REST::AccountSerializer
end
def removed_accounts_search
@accounts = Group.search_for_removed_accounts(@group, params[:q], DEFAULT_ACCOUNTS_LIMIT)
render json: @accounts, each_serializer: REST::AccountSerializer
end
private private
def set_group def set_group

View File

@ -2,6 +2,7 @@ import {
Map as ImmutableMap, Map as ImmutableMap,
List as ImmutableList, List as ImmutableList,
} from 'immutable' } from 'immutable'
import debounce from 'lodash.debounce'
import api, { getLinks } from '../api' import api, { getLinks } from '../api'
import { me } from '../initial_state' import { me } from '../initial_state'
import { importFetchedAccounts } from './importer' import { importFetchedAccounts } from './importer'
@ -33,6 +34,8 @@ export const GROUP_LEAVE_REQUEST = 'GROUP_LEAVE_REQUEST'
export const GROUP_LEAVE_SUCCESS = 'GROUP_LEAVE_SUCCESS' export const GROUP_LEAVE_SUCCESS = 'GROUP_LEAVE_SUCCESS'
export const GROUP_LEAVE_FAIL = 'GROUP_LEAVE_FAIL' export const GROUP_LEAVE_FAIL = 'GROUP_LEAVE_FAIL'
//
export const GROUP_MEMBERS_FETCH_REQUEST = 'GROUP_MEMBERS_FETCH_REQUEST' export const GROUP_MEMBERS_FETCH_REQUEST = 'GROUP_MEMBERS_FETCH_REQUEST'
export const GROUP_MEMBERS_FETCH_SUCCESS = 'GROUP_MEMBERS_FETCH_SUCCESS' export const GROUP_MEMBERS_FETCH_SUCCESS = 'GROUP_MEMBERS_FETCH_SUCCESS'
export const GROUP_MEMBERS_FETCH_FAIL = 'GROUP_MEMBERS_FETCH_FAIL' export const GROUP_MEMBERS_FETCH_FAIL = 'GROUP_MEMBERS_FETCH_FAIL'
@ -41,6 +44,11 @@ export const GROUP_MEMBERS_EXPAND_REQUEST = 'GROUP_MEMBERS_EXPAND_REQUEST'
export const GROUP_MEMBERS_EXPAND_SUCCESS = 'GROUP_MEMBERS_EXPAND_SUCCESS' export const GROUP_MEMBERS_EXPAND_SUCCESS = 'GROUP_MEMBERS_EXPAND_SUCCESS'
export const GROUP_MEMBERS_EXPAND_FAIL = 'GROUP_MEMBERS_EXPAND_FAIL' export const GROUP_MEMBERS_EXPAND_FAIL = 'GROUP_MEMBERS_EXPAND_FAIL'
export const GROUP_MEMBERS_SEARCH_SUCCESS = 'GROUP_MEMBERS_SEARCH_SUCCESS'
export const CLEAR_GROUP_MEMBERS_SEARCH = 'CLEAR_GROUP_MEMBERS_SEARCH'
//
export const GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST = 'GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST' export const GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST = 'GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST'
export const GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS' export const GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS'
export const GROUP_REMOVED_ACCOUNTS_FETCH_FAIL = 'GROUP_REMOVED_ACCOUNTS_FETCH_FAIL' export const GROUP_REMOVED_ACCOUNTS_FETCH_FAIL = 'GROUP_REMOVED_ACCOUNTS_FETCH_FAIL'
@ -49,6 +57,11 @@ export const GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST = 'GROUP_REMOVED_ACCOUNTS_EXP
export const GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS' export const GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS'
export const GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL = 'GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL' export const GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL = 'GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL'
export const GROUP_REMOVED_ACCOUNTS_SEARCH_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_SEARCH_SUCCESS'
export const CLEAR_GROUP_REMOVED_ACCOUNTS_SEARCH = 'CLEAR_GROUP_REMOVED_ACCOUNTS_SEARCH'
//
export const GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST' export const GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST'
export const GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS' export const GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS'
export const GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL = 'GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL' export const GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL = 'GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL'
@ -57,6 +70,8 @@ export const GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_CRE
export const GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS' export const GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS'
export const GROUP_REMOVED_ACCOUNTS_CREATE_FAIL = 'GROUP_REMOVED_ACCOUNTS_CREATE_FAIL' export const GROUP_REMOVED_ACCOUNTS_CREATE_FAIL = 'GROUP_REMOVED_ACCOUNTS_CREATE_FAIL'
//
export const GROUP_JOIN_REQUESTS_FETCH_REQUEST = 'GROUP_JOIN_REQUESTS_FETCH_REQUEST' export const GROUP_JOIN_REQUESTS_FETCH_REQUEST = 'GROUP_JOIN_REQUESTS_FETCH_REQUEST'
export const GROUP_JOIN_REQUESTS_FETCH_SUCCESS = 'GROUP_JOIN_REQUESTS_FETCH_SUCCESS' export const GROUP_JOIN_REQUESTS_FETCH_SUCCESS = 'GROUP_JOIN_REQUESTS_FETCH_SUCCESS'
export const GROUP_JOIN_REQUESTS_FETCH_FAIL = 'GROUP_JOIN_REQUESTS_FETCH_FAIL' export const GROUP_JOIN_REQUESTS_FETCH_FAIL = 'GROUP_JOIN_REQUESTS_FETCH_FAIL'
@ -457,7 +472,7 @@ const expandMembersRequest = (groupId) => ({
type: GROUP_MEMBERS_EXPAND_REQUEST, type: GROUP_MEMBERS_EXPAND_REQUEST,
groupId, groupId,
}) })
``
const expandMembersSuccess = (groupId, accounts, next) => ({ const expandMembersSuccess = (groupId, accounts, next) => ({
type: GROUP_MEMBERS_EXPAND_SUCCESS, type: GROUP_MEMBERS_EXPAND_SUCCESS,
groupId, groupId,
@ -472,6 +487,43 @@ const expandMembersFail = (groupId, error) => ({
error, error,
}) })
/**
*
*/
export const fetchGroupMembersAdminSearch = (groupId, query) => (dispatch, getState) => {
if (!groupId || !query) return
debouncedFetchGroupMembersAdminSearch(groupId, query, dispatch, getState)
}
export const debouncedFetchGroupMembersAdminSearch = debounce((groupId, query, dispatch, getState) => {
if (!groupId || !query) return
api(getState).get(`/api/v1/groups/${groupId}/member_search`, {
params: {
q: query,
resolve: false,
limit: 4,
},
}).then((response) => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchGroupMembersAdminSearchSuccess(response.data))
}).catch((error) => {
//
})
}, 650, { leading: true })
const fetchGroupMembersAdminSearchSuccess = (accounts) => ({
type: GROUP_MEMBERS_SEARCH_SUCCESS,
accounts,
})
/**
*
*/
export const clearGroupMembersAdminSearch = () => (dispatch) => {
dispatch({ type: CLEAR_GROUP_MEMBERS_SEARCH })
}
/** /**
* @description Fetch removed accounts for the given groupId and imports paginated * @description Fetch removed accounts for the given groupId and imports paginated
* accounts and sets in user_lists reducer. * accounts and sets in user_lists reducer.
@ -557,6 +609,43 @@ const expandRemovedAccountsFail = (groupId, error) => ({
error, error,
}) })
/**
*
*/
export const fetchGroupRemovedAccountsAdminSearch = (groupId, query) => (dispatch, getState) => {
if (!groupId || !query) return
debouncedFetchGroupRemovedAccountsAdminSearch(groupId, query, dispatch, getState)
}
export const debouncedFetchGroupRemovedAccountsAdminSearch = debounce((groupId, query, dispatch, getState) => {
if (!groupId || !query) return
api(getState).get(`/api/v1/groups/${groupId}/removed_accounts_search`, {
params: {
q: query,
resolve: false,
limit: 4,
},
}).then((response) => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchGroupRemovedAccountsAdminSearchSuccess(response.data))
}).catch((error) => {
//
})
}, 650, { leading: true })
const fetchGroupRemovedAccountsAdminSearchSuccess = (accounts) => ({
type: GROUP_REMOVED_ACCOUNTS_SEARCH_SUCCESS,
accounts,
})
/**
*
*/
export const clearGroupRemovedAccountsAdminSearch = () => (dispatch) => {
dispatch({ type: CLEAR_GROUP_REMOVED_ACCOUNTS_SEARCH })
}
/** /**
* @description Remove a "removed account" from a group with the given groupId and accountId. * @description Remove a "removed account" from a group with the given groupId and accountId.
* @param {String} groupId * @param {String} groupId

View File

@ -64,7 +64,7 @@ class Account extends ImmutablePureComponent {
) )
} }
const actionButton = (onActionClick && actionIcon) ? ( const actionButton = (onActionClick && (actionIcon || actionTitle)) ? (
<Button <Button
onClick={this.handleAction} onClick={this.handleAction}
isOutline={true} isOutline={true}

View File

@ -1,6 +1,5 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { CX } from '../constants' import { CX } from '../constants'
@ -15,29 +14,45 @@ class Album extends React.PureComponent {
// //
} }
handleOnOpenAlbumCreation = () => {
}
render() { render() {
const { album, isDummy } = this.props const {
album,
isAddable,
isDummy,
} = this.props
const title = isAddable ? 'New album' : 'Album title'
const subtitle = isAddable ? '' : '10 Items'
const to = isAddable ? undefined : `/photos`
return ( return (
<div className={[_s.d, _s.minW162PX, _s.px5, _s.flex1].join(' ')}> <div className={[_s.d, _s.minW162PX, _s.px5, _s.flex1].join(' ')}>
{ {
!isDummy && !isDummy &&
<NavLink <Button
className={[_s.d, _s.noUnderline].join(' ')} noClasses
to='/' className={[_s.d, _s.noUnderline, _s.noOutline, _s.bgTransparent].join(' ')}
to={to}
onClick={isAddable ? this.handleOnOpenAlbumCreation : undefined}
> >
<div className={[_s.d, _s.w100PC, _s.mt5, _s.mb10].join(' ')}> <div className={[_s.d, _s.w100PC, _s.mt5, _s.mb10].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.pt100PC].join(' ')}> <div className={[_s.d, _s.w100PC, _s.pt100PC].join(' ')}>
<div className={[_s.d, _s.posAbs, _s.top0, _s.w100PC, _s.right0, _s.bottom0, _s.left0].join(' ')}> <div className={[_s.d, _s.posAbs, _s.top0, _s.w100PC, _s.right0, _s.bottom0, _s.left0].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.h100PC, _s.radiusSmall, _s.bgTertiary, _s.border1PX, _s.borderColorSecondary].join(' ')} /> <div className={[_s.d, _s.w100PC, _s.h100PC, _s.aiCenter, _s.jcCenter, _s.radiusSmall, _s.bgTertiary, _s.border1PX, _s.borderColorSecondary].join(' ')}>
{ isAddable && <Icon id='add' size='20px' /> }
</div>
</div> </div>
</div> </div>
</div> </div>
<div className={[_s.d, _s.w100PC, _s.pt7, _s.mb15].join(' ')}> <div className={[_s.d, _s.w100PC, _s.pt7, _s.mb15].join(' ')}>
<Text weight='bold'>Profile Photos</Text> <Text weight='bold'>{title}</Text>
<Text color='secondary' size='small' className={_s.mt5}>1 Item</Text> { !isAddable && <Text color='secondary' size='small' className={_s.mt5}>{subtitle}</Text> }
</div> </div>
</NavLink> </Button>
} }
</div> </div>
) )

View File

@ -95,6 +95,8 @@ class MediaItem extends ImmutablePureComponent {
const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`; const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`;
// : todo : fix dimensions to be like albums
return ( return (
<div className={[_s.d, _s.pt25PC].join(' ')}> <div className={[_s.d, _s.pt25PC].join(' ')}>
<div className={containerClasses}> <div className={containerClasses}>

View File

@ -15,55 +15,6 @@ class ToastsContainer extends React.PureComponent {
render() { render() {
const { notifications } = this.props const { notifications } = this.props
console.log("notifications:", notifications)
// const notifications = [
// {
// key: '1',
// title: 'Error',
// to: 'to',
// image: 'https://gab.com/media/user/58077e8a49705.jpg',
// message: 'Unable to follow @andrew',
// date: new Date(),
// isImageAccount: true,
// },
// {
// key: '2',
// title: 'Success',
// to: 'to',
// image: 'https://gab.com/media/user/58077e8a49705.jpg',
// message: 'Your gab was posted. Click here to view',
// date: new Date(),
// isImageAccount: false,
// },
// {
// key: '3',
// title: '',
// to: 'to',
// image: 'https://gab.com/media/user/58077e8a49705.jpg',
// message: 'Unable to follow @andrew',
// date: new Date(),
// isImageAccount: true,
// },
// {
// key: '4',
// title: '',
// to: 'to',
// image: 'https://gab.com/media/user/58077e8a49705.jpg',
// message: 'Your gab was posted. Click here to view',
// date: new Date(),
// isImageAccount: false,
// },
// {
// key: '5',
// title: '',
// to: 'to',
// message: 'Your gab was deleted',
// date: new Date(),
// isImageAccount: false,
// },
// ]
const hasNotifications = !!notifications && notifications.size > 0 const hasNotifications = !!notifications && notifications.size > 0
const containerClasses = CX({ const containerClasses = CX({
@ -76,6 +27,7 @@ class ToastsContainer extends React.PureComponent {
pt15: 1, pt15: 1,
heightMax100VH: 1, heightMax100VH: 1,
pb10: 1, pb10: 1,
saveAreaInsetMB: 1,
displayNone: !hasNotifications displayNone: !hasNotifications
}) })

View File

@ -67,6 +67,13 @@ class AccountAlbums extends ImmutablePureComponent {
// } // }
render() { render() {
const {
account,
isMe,
} = this.props
if (!account) return null
return ( return (
<Block> <Block>
<div className={[_s.d, _s.px10, _s.py10].join(' ')}> <div className={[_s.d, _s.px10, _s.py10].join(' ')}>
@ -76,18 +83,18 @@ class AccountAlbums extends ImmutablePureComponent {
<TabBar tabs={[ <TabBar tabs={[
{ {
title: 'All Photos', title: 'All Photos',
to: '/' to: `/${account.get('username')}/photos`,
}, },
{ {
title: 'Albums', title: 'Albums',
isActive: true, isActive: true,
to: '/' to: `/${account.get('username')}/albums`,
}, },
]}/> ]}/>
</div> </div>
<div className={[_s.d, _s.w100PC, _s.flexRow, _s.flexWrap, _s.px10, _s.mb15, _s.pb10].join(' ')}> <div className={[_s.d, _s.w100PC, _s.flexRow, _s.flexWrap, _s.px10, _s.mb15, _s.pb10].join(' ')}>
<Album /> { isMe && <Album isAddable /> }
<Album /> <Album />
<Album /> <Album />
<Album /> <Album />
@ -111,7 +118,7 @@ class AccountAlbums extends ImmutablePureComponent {
// account, // account,
// } = this.props // } = this.props
// if (!account) return null
// return ( // return (
// <Block> // <Block>

View File

@ -0,0 +1 @@
// : todo :

View File

@ -10,6 +10,7 @@ import { me } from '../initial_state'
import { import {
fetchMembers, fetchMembers,
expandMembers, expandMembers,
fetchGroupMembersAdminSearch,
} from '../actions/groups' } from '../actions/groups'
import { openPopover } from '../actions/popover' import { openPopover } from '../actions/popover'
import Account from '../components/account' import Account from '../components/account'
@ -21,6 +22,10 @@ import ScrollableList from '../components/scrollable_list'
class GroupMembers extends ImmutablePureComponent { class GroupMembers extends ImmutablePureComponent {
state = {
query: '',
}
componentWillMount() { componentWillMount() {
const { groupId } = this.props const { groupId } = this.props
@ -44,20 +49,29 @@ class GroupMembers extends ImmutablePureComponent {
this.props.onExpandMembers(this.props.groupId) this.props.onExpandMembers(this.props.groupId)
}, 300, { leading: true }) }, 300, { leading: true })
handleOnChange = (query) => {
this.setState({ query })
this.props.onChange(this.props.groupId, query)
}
render() { render() {
const { const {
accountIds, listAccountIds,
searchAcountIds,
hasMore, hasMore,
group, group,
relationships, relationships,
} = this.props } = this.props
const { query } = this.state
if (!group || !relationships) return <ColumnIndicator type='loading' /> if (!group || !relationships) return <ColumnIndicator type='loading' />
const isAdminOrMod = relationships ? (relationships.get('admin') || relationships.get('moderator')) : false const isAdminOrMod = relationships ? (relationships.get('admin') || relationships.get('moderator')) : false
if (!isAdminOrMod) return <ColumnIndicator type='missing' /> if (!isAdminOrMod) return <ColumnIndicator type='missing' />
const accountIds = !!query ? searchAcountIds : listAccountIds
return ( return (
<Block> <Block>
<BlockHeading title='Group Members' /> <BlockHeading title='Group Members' />
@ -67,11 +81,8 @@ class GroupMembers extends ImmutablePureComponent {
id='group-member-search' id='group-member-search'
placeholder='Search group members' placeholder='Search group members'
prependIcon='search' prependIcon='search'
// value={value} value={query}
onKeyUp={this.handleKeyUp}
onChange={this.handleOnChange} onChange={this.handleOnChange}
onFocus={this.handleOnFocus}
onBlur={this.handleOnBlur}
autoComplete='off' autoComplete='off'
/> />
</div> </div>
@ -109,17 +120,21 @@ class GroupMembers extends ImmutablePureComponent {
const mapStateToProps = (state, { params }) => { const mapStateToProps = (state, { params }) => {
const groupId = isObject(params) ? params['id'] : -1 const groupId = isObject(params) ? params['id'] : -1
const group = groupId === -1 ? null : state.getIn(['groups', groupId]) const group = groupId === -1 ? null : state.getIn(['groups', groupId])
return { return {
group, group,
groupId, groupId,
listAccountIds: state.getIn(['user_lists', 'groups', groupId, 'items']),
searchAcountIds: state.getIn(['group_lists', 'member_search_accounts']),
relationships: state.getIn(['group_relationships', groupId]), relationships: state.getIn(['group_relationships', groupId]),
accountIds: state.getIn(['user_lists', 'groups', groupId, 'items']),
hasMore: !!state.getIn(['user_lists', 'groups', groupId, 'next']), hasMore: !!state.getIn(['user_lists', 'groups', groupId, 'next']),
} }
} }
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onChange(groupId, query) {
dispatch(fetchGroupMembersAdminSearch(groupId, query))
},
onFetchMembers(groupId) { onFetchMembers(groupId) {
dispatch(fetchMembers(groupId)) dispatch(fetchMembers(groupId))
}, },

View File

@ -3,13 +3,13 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import isObject from 'lodash.isobject' import isObject from 'lodash.isobject'
import { import {
fetchRemovedAccounts, fetchRemovedAccounts,
expandRemovedAccounts, expandRemovedAccounts,
removeRemovedAccount, removeRemovedAccount,
fetchGroupRemovedAccountsAdminSearch,
} from '../actions/groups' } from '../actions/groups'
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
import Account from '../components/account' import Account from '../components/account'
@ -21,6 +21,10 @@ import ScrollableList from '../components/scrollable_list'
class GroupRemovedAccounts extends ImmutablePureComponent { class GroupRemovedAccounts extends ImmutablePureComponent {
state = {
query: '',
}
componentWillMount() { componentWillMount() {
const { groupId } = this.props const { groupId } = this.props
@ -37,16 +41,24 @@ class GroupRemovedAccounts extends ImmutablePureComponent {
this.props.onExpandRemovedAccounts(this.props.groupId) this.props.onExpandRemovedAccounts(this.props.groupId)
}, 300, { leading: true }) }, 300, { leading: true })
handleOnChange = (query) => {
this.setState({ query })
this.props.onChange(this.props.groupId, query)
}
render() { render() {
const { const {
accountIds, listAccountIds,
searchAcountIds,
hasMore, hasMore,
group, group,
intl,
} = this.props } = this.props
const { query } = this.state
if (!group) return <ColumnIndicator type='loading' /> if (!group) return <ColumnIndicator type='loading' />
const accountIds = !!query ? searchAcountIds : listAccountIds
return ( return (
<Block> <Block>
<BlockHeading title='Removed Accounts' /> <BlockHeading title='Removed Accounts' />
@ -55,11 +67,8 @@ class GroupRemovedAccounts extends ImmutablePureComponent {
id='group-member-search' id='group-member-search'
placeholder='Search removed group members' placeholder='Search removed group members'
prependIcon='search' prependIcon='search'
// value={value} value={query}
onKeyUp={this.handleKeyUp}
onChange={this.handleOnChange} onChange={this.handleOnChange}
onFocus={this.handleOnFocus}
onBlur={this.handleOnBlur}
autoComplete='off' autoComplete='off'
/> />
</div> </div>
@ -73,11 +82,11 @@ class GroupRemovedAccounts extends ImmutablePureComponent {
{ {
accountIds && accountIds.map((id) => ( accountIds && accountIds.map((id) => (
<Account <Account
compact
key={id} key={id}
id={id} id={id}
actionIcon='subtract'
onActionClick={() => this.props.onRemoveRemovedAccount(group.get('id'), id)} onActionClick={() => this.props.onRemoveRemovedAccount(group.get('id'), id)}
actionTitle={intl.formatMessage(messages.remove)} actionTitle='Allow to join'
/> />
)) ))
} }
@ -88,10 +97,6 @@ class GroupRemovedAccounts extends ImmutablePureComponent {
} }
const messages = defineMessages({
remove: { id: 'groups.removed_accounts', defaultMessage: 'Allow joining' },
})
const mapStateToProps = (state, { params }) => { const mapStateToProps = (state, { params }) => {
const groupId = isObject(params) ? params['id'] : -1 const groupId = isObject(params) ? params['id'] : -1
const group = groupId === -1 ? null : state.getIn(['groups', groupId]) const group = groupId === -1 ? null : state.getIn(['groups', groupId])
@ -99,12 +104,16 @@ const mapStateToProps = (state, { params }) => {
return { return {
group, group,
groupId, groupId,
accountIds: state.getIn(['user_lists', 'group_removed_accounts', groupId, 'items']), listAccountIds: state.getIn(['user_lists', 'group_removed_accounts', groupId, 'items']),
searchAcountIds: state.getIn(['group_lists', 'removed_search_accounts']),
hasMore: !!state.getIn(['user_lists', 'group_removed_accounts', groupId, 'next']), hasMore: !!state.getIn(['user_lists', 'group_removed_accounts', groupId, 'next']),
} }
} }
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onChange(groupId, query) {
dispatch(fetchGroupRemovedAccountsAdminSearch(groupId, query))
},
onFetchRemovedAccounts(groupId) { onFetchRemovedAccounts(groupId) {
dispatch(fetchRemovedAccounts(groupId)) dispatch(fetchRemovedAccounts(groupId))
}, },
@ -125,4 +134,4 @@ GroupRemovedAccounts.propTypes = {
onRemoveRemovedAccount: PropTypes.func.isRequired, onRemoveRemovedAccount: PropTypes.func.isRequired,
} }
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(GroupRemovedAccounts)) export default connect(mapStateToProps, mapDispatchToProps)(GroupRemovedAccounts)

View File

@ -174,7 +174,7 @@ class ChatMessageItem extends ImmutablePureComponent {
!!expirationDate && !!expirationDate &&
<React.Fragment> <React.Fragment>
<DotTextSeperator /> <DotTextSeperator />
<Text size='extraSmall' color='tertiary' className={_s.ml5}>Expires in {timeUntilExpiration}</Text> <Text size='extraSmall' color='tertiary' className={_s.ml5}>Expires {timeUntilExpiration}</Text>
<Icon id='stopwatch' size='11px' className={[_s.d, _s.ml5, _s.displayInline, _s.cSecondary].join(' ')} /> <Icon id='stopwatch' size='11px' className={[_s.d, _s.ml5, _s.displayInline, _s.cSecondary].join(' ')} />
</React.Fragment> </React.Fragment>
} }

View File

@ -76,7 +76,7 @@ class MessagesLayout extends React.PureComponent {
actions={[ actions={[
{ {
icon: 'add', icon: 'add',
to: '/messages/new', to: `'/messages/new'`,
}, },
{ {
icon: 'cog', icon: 'cog',

View File

@ -16,6 +16,10 @@ import {
GROUPS_BY_TAG_FETCH_REQUEST, GROUPS_BY_TAG_FETCH_REQUEST,
GROUPS_BY_TAG_FETCH_SUCCESS, GROUPS_BY_TAG_FETCH_SUCCESS,
GROUPS_BY_TAG_FETCH_FAIL, GROUPS_BY_TAG_FETCH_FAIL,
GROUP_MEMBERS_SEARCH_SUCCESS,
CLEAR_GROUP_MEMBERS_SEARCH,
GROUP_REMOVED_ACCOUNTS_SEARCH_SUCCESS,
CLEAR_GROUP_REMOVED_ACCOUNTS_SEARCH,
} from '../actions/groups' } from '../actions/groups'
import { import {
GROUP_TIMELINE_SORTING_TYPE_TOP, GROUP_TIMELINE_SORTING_TYPE_TOP,
@ -51,6 +55,8 @@ const initialState = ImmutableMap({
}), }),
by_category: ImmutableMap(), by_category: ImmutableMap(),
by_tag: ImmutableMap(), by_tag: ImmutableMap(),
member_search_accounts: ImmutableList(),
removed_search_accounts: ImmutableList(),
}) })
export default function groupLists(state = initialState, action) { export default function groupLists(state = initialState, action) {
@ -144,6 +150,14 @@ export default function groupLists(state = initialState, action) {
items: ImmutableList(), items: ImmutableList(),
isLoading: false, isLoading: false,
})) }))
case GROUP_MEMBERS_SEARCH_SUCCESS:
return state.set('member_search_accounts', ImmutableList(action.accounts.map((item) => item.id)))
case CLEAR_GROUP_MEMBERS_SEARCH:
return state.set('member_search_accounts', ImmutableList())
case GROUP_REMOVED_ACCOUNTS_SEARCH_SUCCESS:
return state.set('removed_search_accounts', ImmutableList(action.accounts.map((item) => item.id)))
case CLEAR_GROUP_REMOVED_ACCOUNTS_SEARCH:
return state.set('removed_search_accounts', ImmutableList())
default: default:
return state return state
} }

View File

@ -404,7 +404,7 @@ class Account < ApplicationRecord
records records
end end
def advanced_search_for(terms, account, limit = 10, following = false, offset = 0, options = {}) def advanced_search_for(terms, account, limit = 10, offset = 0, options = {})
textsearch, query = generate_query_for_search(terms) textsearch, query = generate_query_for_search(terms)
@onlyVerified = options[:onlyVerified] || false @onlyVerified = options[:onlyVerified] || false
@ -426,7 +426,6 @@ class Account < ApplicationRecord
records = find_by_sql([sql, account.id, account.id, limit, offset]) records = find_by_sql([sql, account.id, account.id, limit, offset])
ActiveRecord::Associations::Preloader.new.preload(records, :account_stat) ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
records records
end end

View File

@ -70,6 +70,16 @@ class Group < ApplicationRecord
.limit(25) .limit(25)
.offset(offset) .offset(offset)
end end
def search_for_members(group, term, limit)
pattern = '%' + sanitize_sql_like(term.strip) + '%'
group.accounts.where("LOWER(username) LIKE LOWER(?)", pattern).limit(limit)
end
def search_for_removed_accounts(group, term, limit)
pattern = '%' + sanitize_sql_like(term.strip) + '%'
group.removed_accounts.where("LOWER(username) LIKE LOWER(?)", pattern).limit(limit)
end
end end
def has_password? def has_password?

View File

@ -4,13 +4,13 @@ class AccountSearchService < BaseService
attr_reader :query, :limit, :offset, :options, :account attr_reader :query, :limit, :offset, :options, :account
def call(query, account = nil, options = {}) def call(query, account = nil, options = {})
puts "query:"+query.inspect
@query = query.strip @query = query.strip
@limit = options[:limit].to_i @limit = options[:limit].to_i
@offset = options[:offset].to_i @offset = options[:offset].to_i
@onlyVerified = options[:onlyVerified] || false @onlyVerified = options[:onlyVerified] || false
@options = options @options = options
@account = account @account = account
@group = options[:group] || nil
search_service_results search_service_results
end end
@ -84,7 +84,7 @@ class AccountSearchService < BaseService
end end
def advanced_search_results def advanced_search_results
Account.advanced_search_for(terms_for_query, account, limit, options[:following], offset, onlyVerified: @onlyVerified) Account.advanced_search_for(terms_for_query, account, limit, offset, onlyVerified: @onlyVerified)
end end
def simple_search_results def simple_search_results

View File

@ -349,6 +349,9 @@ Rails.application.routes.draw do
member do member do
delete '/statuses/:status_id', to: 'groups#destroy_status' delete '/statuses/:status_id', to: 'groups#destroy_status'
post '/statuses/:status_id/approve', to: 'groups#approve_status' post '/statuses/:status_id/approve', to: 'groups#approve_status'
get '/member_search', to: 'groups#member_search'
get '/removed_accounts_search', to: 'groups#removed_accounts_search'
end end
get '/category/:category', to: 'groups#by_category' get '/category/:category', to: 'groups#by_category'