Skip to content

Commit 33c3dd3

Browse files
committed
Add permissions/member list screen and various fixes
1 parent a4d14a4 commit 33c3dd3

45 files changed

Lines changed: 4302 additions & 1092 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/mobile/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { PossibleImpersonationAttackScreen } from './screens/PossibleImpersonati
5757
import UsernameTakenScreen from './screens/UsernameTaken/UsernameTaken.screen'
5858
import { CaptchaDrawer } from './components/ModalBottomDrawer/drawers/Captcha.drawer'
5959
import { ChannelMembershipScreen } from './screens/ChannelMembership/ChannelMembership.screen'
60+
import { UpdateChannelMembershipScreen } from './screens/ChannelMembership/UpdateChannelMembership/UpdateChannelMembership.screen'
6061

6162
const logger = createLogger('app')
6263

@@ -111,6 +112,7 @@ function App(): JSX.Element {
111112
<Screen component={ConnectionProcessScreen} name={ScreenNames.ConnectionProcessScreen} />
112113
<Screen component={DeleteChannelScreen} name={ScreenNames.DeleteChannelScreen} />
113114
<Screen component={ChannelMembershipScreen} name={ScreenNames.ChannelMembershipScreen} />
115+
<Screen component={UpdateChannelMembershipScreen} name={ScreenNames.UpdateChannelMembershipScreen} />
114116
<Screen component={ErrorScreen} name={ScreenNames.ErrorScreen} />
115117
<Screen component={DuplicatedUsernameScreen} name={ScreenNames.DuplicatedUsernameScreen} />
116118
<Screen component={UsernameTakenScreen} name={ScreenNames.UsernameTakenScreen} />

packages/mobile/src/components/Appbar/Appbar.component.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { FC } from 'react'
2-
import { View, Image, TouchableOpacity } from 'react-native'
2+
import { View, Image, TouchableOpacity, Keyboard } from 'react-native'
33
import { Typography } from '../Typography/Typography.component'
44
import { StyledAppbar } from './Appbar.styles'
55
import { AppbarProps } from './Appbar.types'
@@ -14,6 +14,7 @@ export const Appbar: FC<AppbarProps> = ({
1414
position,
1515
style,
1616
back,
17+
submit,
1718
contextMenu,
1819
crossBackIcon = false,
1920
}) => {
@@ -75,6 +76,7 @@ export const Appbar: FC<AppbarProps> = ({
7576
<TouchableOpacity
7677
onPress={event => {
7778
event.persist()
79+
Keyboard.dismiss()
7880
contextMenu.handleOpen()
7981
}}
8082
testID={'open_menu'}
@@ -92,6 +94,21 @@ export const Appbar: FC<AppbarProps> = ({
9294
</View>
9395
</TouchableOpacity>
9496
)}
97+
{submit && (
98+
<TouchableOpacity
99+
onPress={event => {
100+
event.persist()
101+
submit()
102+
}}
103+
testID={'submit'}
104+
>
105+
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
106+
<Typography style={{ color: defaultTheme.palette.typography.blue }} fontSize={16}>
107+
Done
108+
</Typography>
109+
</View>
110+
</TouchableOpacity>
111+
)}
95112
</View>
96113
</StyledAppbar>
97114
)

packages/mobile/src/components/Appbar/Appbar.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface AppbarProps {
1010
position?: 'flex-start' | 'center'
1111
style?: TextStyle
1212
back?: () => void
13+
submit?: () => void
1314
contextMenu?: ReturnType<typeof useContextMenu> | null
1415
crossBackIcon?: boolean
1516
}

packages/mobile/src/components/Button/Button.component.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import * as Progress from 'react-native-progress'
66
import { Typography } from '../Typography/Typography.component'
77
import { defaultTheme } from '../../styles/themes/default.theme'
88

9-
export const Button: FC<ButtonProps> = ({ onPress, title, width, loading, negative, disabled, newDesign }) => {
9+
export const Button: FC<ButtonProps> = ({ onPress, title, width, loading, negative, disabled, newDesign, testID }) => {
1010
return (
1111
<TouchableWithoutFeedback
1212
onPress={event => {
1313
// event.persist()
1414
if (!disabled) onPress()
1515
}}
16-
testID={'button'}
16+
testID={testID ?? 'button'}
1717
>
1818
<View
1919
style={{

packages/mobile/src/components/Button/Button.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export interface ButtonProps {
66
negative?: boolean
77
disabled?: boolean
88
newDesign?: boolean
9+
testID?: string
910
}

packages/mobile/src/components/ChannelMembership/ChannelMembership.component.tsx

Lines changed: 69 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,54 @@
1-
import React, { useEffect, useRef, useState } from 'react'
2-
import { KeyboardAvoidingView, TextInput, View } from 'react-native'
1+
import React, { useCallback, useEffect, useState } from 'react'
2+
import { KeyboardAvoidingView, Platform, View } from 'react-native'
33

44
import { defaultPalette } from '../../styles/palettes/default.palette'
55
import { Appbar } from '../Appbar/Appbar.component'
66
import { Button } from '../Button/Button.component'
77
import { ChannelMembershipProps } from './ChannelMembership.types'
88
import { createLogger } from '../../utils/logger'
99
import { ChannelMembershipAppbarHeaderTitle } from './ChannelMembershipAppbarHeaderTitle.component'
10-
import { SelectableListOption } from './ChannelMembershipList/ChannelMembershipList.types'
11-
import { ChannelMembershipList } from './ChannelMembershipList/ChannelMembershipList.component'
12-
import { Typography } from '../Typography/Typography.component'
10+
import { ChannelMembershipList } from './ChannelMembershipList.component'
1311
import { defaultTheme } from '../../styles/themes/default.theme'
14-
import { Input } from '../Input/Input.component'
15-
import Fuse from 'fuse.js'
12+
import { useDispatch, useSelector } from 'react-redux'
13+
import { navigationActions } from '../../store/navigation/navigation.slice'
14+
import { ScreenNames } from '../../const/ScreenNames.enum'
15+
import { communities } from '@quiet/state-manager'
1616

1717
const logger = createLogger('ChannelMembership')
1818

19+
const TITLE = 'Permissions'
20+
const NON_OWNER_TITLE = 'Members'
21+
1922
export const ChannelMembership: React.FC<ChannelMembershipProps> = ({
2023
channelName,
2124
channelId,
22-
userProfiles = {},
2325
community,
24-
updateChannelMembership,
26+
members,
27+
memberCount,
2528
handleBackButton,
2629
}) => {
30+
const dispatch = useDispatch()
2731
const [displayedName, setDisplayedName] = useState<string>('')
2832
const [loading, setLoading] = useState<boolean>(false)
29-
const [options, setOptions] = useState<SelectableListOption[]>([])
30-
const [visibleOptionIndices, setVisibleOptionIndices] = useState<Set<number>>(new Set())
31-
const [inputError, setInputError] = useState<string | undefined>()
32-
const [membershipSearchInput, setMembershipSearchInput] = useState<string | undefined>()
33-
const [fuzzySearch, setFuzzySearch] = useState<Fuse<SelectableListOption> | undefined>()
34-
const inputRef = useRef<TextInput>(null)
33+
const [headerTitle, setHeaderTitle] = useState<string>('')
3534

36-
const _initializeOptions = () => {
37-
const initialOptions: SelectableListOption[] = []
38-
const visibleIndices: Set<number> = new Set()
39-
let index = 0
40-
for (const user of Object.values(userProfiles)) {
41-
let mutable = true
42-
let selected = false
43-
if ((user.channels ?? []).includes(channelId)) {
44-
mutable = false
45-
selected = true
46-
}
47-
initialOptions.push({ label: user.nickname, id: user.userId, selected, index, mutable, hide: false })
48-
visibleIndices.add(index)
49-
index++
50-
}
51-
setOptions(initialOptions)
52-
setVisibleOptionIndices(visibleIndices)
53-
setFuzzySearch(
54-
new Fuse(initialOptions, {
55-
keys: ['label'],
56-
minMatchCharLength: 1,
57-
ignoreDiacritics: true,
58-
threshold: 0.3,
59-
})
60-
)
61-
}
35+
const isOwner = useSelector(communities.selectors.isOwner)
6236

63-
const onPress = () => {
37+
const onPress = useCallback(() => {
6438
setLoading(true)
65-
updateChannelMembership(options.filter(option => option.selected && option.mutable).map(option => option.id))
66-
setOptions([])
67-
setVisibleOptionIndices(new Set())
68-
}
39+
dispatch(
40+
navigationActions.replaceScreen({
41+
screen: ScreenNames.UpdateChannelMembershipScreen,
42+
params: {
43+
channelName,
44+
channelId,
45+
},
46+
})
47+
)
48+
}, [dispatch, channelName, channelId])
6949

7050
const goBack = () => {
7151
if (!loading) {
72-
setOptions([])
7352
handleBackButton()
7453
}
7554
}
@@ -78,82 +57,67 @@ export const ChannelMembership: React.FC<ChannelMembershipProps> = ({
7857
useEffect(() => {
7958
if (channelName !== '') {
8059
setDisplayedName(channelName)
81-
_initializeOptions()
8260
}
8361
}, [channelName])
8462

85-
const _setAllOptionsVisible = () => {
86-
return new Set(Array(options.length).keys())
87-
}
88-
89-
const _fuzzyFilterUsers = (filterText: string): Set<number> => {
90-
if (fuzzySearch == null) {
91-
return _setAllOptionsVisible()
92-
}
93-
const searchResults = fuzzySearch.search(filterText)
94-
return new Set(searchResults.map(result => result.item.index))
95-
}
96-
97-
const onChangeText = (value: string) => {
98-
setInputError(undefined)
99-
setMembershipSearchInput(value)
100-
if (value === '') {
101-
setVisibleOptionIndices(_setAllOptionsVisible())
102-
return
103-
}
104-
const foundIndices = _fuzzyFilterUsers(value)
105-
setVisibleOptionIndices(foundIndices)
106-
}
63+
useEffect(() => {
64+
setHeaderTitle(isOwner ? TITLE : NON_OWNER_TITLE)
65+
}, [isOwner])
10766

10867
return (
109-
<View style={{ flex: 1, backgroundColor: defaultPalette.background.white }} testID={'channel-membership-component'}>
68+
<View
69+
style={{ flex: 1, backgroundColor: defaultPalette.background.white }}
70+
testID={`channel-membership-component-${channelId}`}
71+
>
11072
<KeyboardAvoidingView
111-
behavior='height'
73+
behavior={Platform.select({ ios: 'padding', android: 'height' })}
11274
style={{
11375
flex: 1,
114-
marginTop: 24,
115-
paddingLeft: 20,
116-
paddingRight: 20,
11776
marginBottom: 16,
11877
}}
11978
>
12079
<Appbar
121-
title={'Add members'}
122-
titleComponent={<ChannelMembershipAppbarHeaderTitle title={'Add members'} channelName={displayedName} />}
80+
title={headerTitle}
81+
titleComponent={
82+
<ChannelMembershipAppbarHeaderTitle
83+
title={headerTitle}
84+
channelName={displayedName}
85+
membershipCount={memberCount}
86+
/>
87+
}
12388
back={goBack}
12489
/>
12590
<View
12691
style={{
127-
padding: 24,
92+
paddingTop: 16,
93+
display: 'flex',
94+
flexDirection: 'column',
95+
gap: 32,
12896
}}
12997
>
130-
<Input
131-
onChangeText={onChangeText}
132-
subtitle={`Add members with '@'`}
133-
placeholder={'E.g. @jane123'}
134-
value={membershipSearchInput}
135-
length={20}
136-
disabled={loading}
137-
validation={inputError}
138-
ref={inputRef}
139-
autoCorrect={false}
140-
/>
141-
<Typography fontSize={10} style={{ paddingTop: 18, color: defaultTheme.palette.typography.gray50 }}>
142-
MEMBERS
143-
</Typography>
144-
<ChannelMembershipList
145-
options={options}
146-
visibleOptionsIndices={visibleOptionIndices}
147-
setOptions={setOptions}
148-
channelId={channelId}
149-
userProfiles={userProfiles}
150-
/>
151-
<View style={{ paddingTop: 16 + 12 }}>
152-
<Button title={'Update Channel Membership'} onPress={onPress} loading={loading} />
153-
</View>
154-
<View style={{ paddingTop: 24 }}>
155-
<Button title={'Never mind'} onPress={goBack} negative />
156-
</View>
98+
{isOwner && (
99+
<View>
100+
<View
101+
style={{
102+
width: 'auto',
103+
display: 'flex',
104+
flexDirection: 'row',
105+
alignItems: 'flex-end',
106+
alignSelf: 'flex-end',
107+
paddingHorizontal: 16,
108+
paddingBottom: 16,
109+
}}
110+
>
111+
<Button
112+
title={'Add members'}
113+
onPress={onPress}
114+
testID={`channel-membership-component-add-members-${channelId}`}
115+
/>
116+
</View>
117+
<View style={{ height: 1, backgroundColor: defaultTheme.palette.background.gray06 }} />
118+
</View>
119+
)}
120+
<ChannelMembershipList members={members} channelId={channelId} />
157121
</View>
158122
</KeyboardAvoidingView>
159123
</View>

packages/mobile/src/components/ChannelMembership/ChannelMembership.stories.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ storiesOf('ChannelMembership', module).add('Default', () => (
1111
<ChannelMembership
1212
channelName={'private-channel'}
1313
channelId={'abc123'}
14-
updateChannelMembership={(memberIds: string[]) => {
15-
logger.info('updating channel membership')
16-
}}
14+
userProfiles={{}}
15+
members={undefined}
16+
memberCount={undefined}
1717
handleBackButton={() => {
1818
logger.info('going back')
1919
}}

0 commit comments

Comments
 (0)