Changeset View
Changeset View
Standalone View
Standalone View
native/invite-links/manage-public-link-screen.react.js
// @flow | // @flow | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { Text, View } from 'react-native'; | import { Text, View, Alert } from 'react-native'; | ||||
import { | import { | ||||
createOrUpdatePublicLink, | createOrUpdatePublicLink, | ||||
createOrUpdatePublicLinkActionTypes, | createOrUpdatePublicLinkActionTypes, | ||||
disableInviteLink as callDisableInviteLink, | |||||
disableInviteLinkLinkActionTypes, | |||||
} from 'lib/actions/link-actions.js'; | } from 'lib/actions/link-actions.js'; | ||||
import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js'; | import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js'; | ||||
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; | import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; | ||||
import type { ThreadInfo } from 'lib/types/thread-types.js'; | import type { ThreadInfo } from 'lib/types/thread-types.js'; | ||||
import { | import { | ||||
useDispatchActionPromise, | useDispatchActionPromise, | ||||
useServerCall, | useServerCall, | ||||
} from 'lib/utils/action-utils.js'; | } from 'lib/utils/action-utils.js'; | ||||
Show All 19 Lines | function ManagePublicLinkScreen(props: Props): React.Node { | ||||
const inviteLink = useSelector(primaryInviteLinksSelector)[community.id]; | const inviteLink = useSelector(primaryInviteLinksSelector)[community.id]; | ||||
const [name, setName] = React.useState( | const [name, setName] = React.useState( | ||||
inviteLink?.name ?? Math.random().toString(36).slice(-9), | inviteLink?.name ?? Math.random().toString(36).slice(-9), | ||||
); | ); | ||||
const [error, setError] = React.useState(null); | const [error, setError] = React.useState(null); | ||||
const dispatchActionPromise = useDispatchActionPromise(); | const dispatchActionPromise = useDispatchActionPromise(); | ||||
const callCreateOrUpdatePublicLink = useServerCall(createOrUpdatePublicLink); | const callCreateOrUpdatePublicLink = useServerCall(createOrUpdatePublicLink); | ||||
const createActionPromise = React.useCallback(async () => { | const createCreateOrUpdateActionPromise = React.useCallback(async () => { | ||||
setError(null); | setError(null); | ||||
try { | try { | ||||
return await callCreateOrUpdatePublicLink({ | return await callCreateOrUpdatePublicLink({ | ||||
name, | name, | ||||
communityID: community.id, | communityID: community.id, | ||||
}); | }); | ||||
} catch (e) { | } catch (e) { | ||||
setError(e.message); | setError(e.message); | ||||
throw e; | throw e; | ||||
} | } | ||||
}, [callCreateOrUpdatePublicLink, community.id, name]); | }, [callCreateOrUpdatePublicLink, community.id, name]); | ||||
const createInviteLink = React.useCallback(() => { | const createInviteLink = React.useCallback(() => { | ||||
dispatchActionPromise( | dispatchActionPromise( | ||||
createOrUpdatePublicLinkActionTypes, | createOrUpdatePublicLinkActionTypes, | ||||
createActionPromise(), | createCreateOrUpdateActionPromise(), | ||||
); | ); | ||||
}, [createActionPromise, dispatchActionPromise]); | }, [createCreateOrUpdateActionPromise, dispatchActionPromise]); | ||||
const createOrUpdatePublicLinkStatus = useSelector( | const createOrUpdatePublicLinkStatus = useSelector( | ||||
createOrUpdatePublicLinkStatusSelector, | createOrUpdatePublicLinkStatusSelector, | ||||
); | ); | ||||
const styles = useStyles(unboundStyles); | const styles = useStyles(unboundStyles); | ||||
let errorComponent = null; | let errorComponent = null; | ||||
if (error) { | if (error) { | ||||
errorComponent = <Text style={styles.error}>{error}</Text>; | errorComponent = <Text style={styles.error}>{error}</Text>; | ||||
} | } | ||||
const disableInviteLinkServerCall = useServerCall(callDisableInviteLink); | |||||
const createDisableLinkActionPromise = React.useCallback(async () => { | |||||
setError(null); | |||||
try { | |||||
return await disableInviteLinkServerCall({ | |||||
name, | |||||
communityID: community.id, | |||||
}); | |||||
} catch (e) { | |||||
setError(e.message); | |||||
throw e; | |||||
} | |||||
}, [disableInviteLinkServerCall, community.id, name]); | |||||
const disableInviteLink = React.useCallback(() => { | |||||
dispatchActionPromise( | |||||
disableInviteLinkLinkActionTypes, | |||||
createDisableLinkActionPromise(), | |||||
); | |||||
}, [createDisableLinkActionPromise, dispatchActionPromise]); | |||||
const disableInviteLinkStatus = useSelector(disableInviteLinkStatusSelector); | |||||
const isLoading = | |||||
createOrUpdatePublicLinkStatus === 'loading' || | |||||
disableInviteLinkStatus === 'loading'; | |||||
const onDisableButtonClick = React.useCallback(() => { | |||||
Alert.alert( | |||||
'Disable public link', | |||||
'Are you sure you want to disable your public link? Members who have your community’s public link but have not joined will not able to with the disabled link. \n' + | |||||
'\n' + | |||||
'Other communities may also claim your previous public link url.', | |||||
[ | |||||
{ | |||||
text: 'Confirm disable', | |||||
style: 'destructive', | |||||
onPress: disableInviteLink, | |||||
}, | |||||
{ | |||||
text: 'Cancel', | |||||
}, | |||||
], | |||||
{ | |||||
cancelable: true, | |||||
}, | |||||
); | |||||
}, [disableInviteLink]); | |||||
let disablePublicLinkButton = null; | |||||
if (inviteLink) { | |||||
disablePublicLinkButton = ( | |||||
<View style={styles.destructiveButtonContainer}> | |||||
<Button | |||||
style={[styles.button, styles.destructiveButton]} | |||||
onPress={onDisableButtonClick} | |||||
disabled={isLoading} | |||||
> | |||||
<Text style={styles.destructiveButtonText}>Disable public link</Text> | |||||
</Button> | |||||
</View> | |||||
); | |||||
} | |||||
return ( | return ( | ||||
<View> | <View> | ||||
<View style={styles.section}> | <View style={styles.section}> | ||||
<Text style={styles.sectionText}> | <Text style={styles.sectionText}> | ||||
Let your community be more accessible with your own unique public | Let your community be more accessible with your own unique public | ||||
link. By enabling a public link, you are allowing anyone who has your | link. By enabling a public link, you are allowing anyone who has your | ||||
link to join your community.{'\n\n'} | link to join your community.{'\n\n'} | ||||
Editing your community’s public link allows other communities to claim | Editing your community’s public link allows other communities to claim | ||||
your previous URL. | your previous URL. | ||||
</Text> | </Text> | ||||
</View> | </View> | ||||
<Text style={styles.sectionTitle}>INVITE URL</Text> | <Text style={styles.sectionTitle}>INVITE URL</Text> | ||||
<View style={styles.section}> | <View style={styles.section}> | ||||
<View style={styles.inviteLink}> | <View style={styles.inviteLink}> | ||||
<Text style={styles.inviteLinkPrefix}>https://comm.app/invite/</Text> | <Text style={styles.inviteLinkPrefix}>https://comm.app/invite/</Text> | ||||
<TextInput | <TextInput | ||||
style={styles.input} | style={styles.input} | ||||
value={name} | value={name} | ||||
onChangeText={setName} | onChangeText={setName} | ||||
autoCorrect={false} | autoCorrect={false} | ||||
autoCapitalize="none" | autoCapitalize="none" | ||||
keyboardType="ascii-capable" | keyboardType="ascii-capable" | ||||
editable={createOrUpdatePublicLinkStatus !== 'loading'} | editable={!isLoading} | ||||
/> | /> | ||||
</View> | </View> | ||||
{errorComponent} | {errorComponent} | ||||
<Button | <Button | ||||
style={[styles.button, styles.buttonPrimary]} | style={[styles.button, styles.buttonPrimary]} | ||||
onPress={createInviteLink} | onPress={createInviteLink} | ||||
disabled={createOrUpdatePublicLinkStatus === 'loading'} | disabled={isLoading} | ||||
> | > | ||||
<Text style={styles.buttonText}>Save & enable public link</Text> | <Text style={styles.buttonText}>Save & enable public link</Text> | ||||
</Button> | </Button> | ||||
</View> | </View> | ||||
{disablePublicLinkButton} | |||||
</View> | </View> | ||||
); | ); | ||||
} | } | ||||
const createOrUpdatePublicLinkStatusSelector = createLoadingStatusSelector( | const createOrUpdatePublicLinkStatusSelector = createLoadingStatusSelector( | ||||
createOrUpdatePublicLinkActionTypes, | createOrUpdatePublicLinkActionTypes, | ||||
); | ); | ||||
const disableInviteLinkStatusSelector = createLoadingStatusSelector( | |||||
disableInviteLinkLinkActionTypes, | |||||
); | |||||
const unboundStyles = { | const unboundStyles = { | ||||
sectionTitle: { | sectionTitle: { | ||||
fontSize: 14, | fontSize: 14, | ||||
fontWeight: '400', | fontWeight: '400', | ||||
lineHeight: 20, | lineHeight: 20, | ||||
color: 'modalBackgroundLabel', | color: 'modalBackgroundLabel', | ||||
paddingHorizontal: 16, | paddingHorizontal: 16, | ||||
Show All 39 Lines | button: { | ||||
borderRadius: 8, | borderRadius: 8, | ||||
paddingVertical: 12, | paddingVertical: 12, | ||||
paddingHorizontal: 24, | paddingHorizontal: 24, | ||||
marginTop: 8, | marginTop: 8, | ||||
}, | }, | ||||
buttonPrimary: { | buttonPrimary: { | ||||
backgroundColor: 'purpleButton', | backgroundColor: 'purpleButton', | ||||
}, | }, | ||||
destructiveButtonContainer: { | |||||
margin: 16, | |||||
}, | |||||
destructiveButton: { | |||||
borderWidth: 1, | |||||
borderRadius: 8, | |||||
borderColor: 'vibrantRedButton', | |||||
}, | |||||
destructiveButtonText: { | |||||
fontSize: 16, | |||||
fontWeight: '500', | |||||
lineHeight: 24, | |||||
color: 'vibrantRedButton', | |||||
textAlign: 'center', | |||||
}, | |||||
buttonText: { | buttonText: { | ||||
color: 'whiteText', | color: 'whiteText', | ||||
textAlign: 'center', | textAlign: 'center', | ||||
fontWeight: '500', | fontWeight: '500', | ||||
fontSize: 16, | fontSize: 16, | ||||
lineHeight: 24, | lineHeight: 24, | ||||
}, | }, | ||||
error: { | error: { | ||||
Show All 9 Lines |