diff options
Diffstat (limited to 'src/view/com/modals')
-rw-r--r-- | src/view/com/modals/InviteCodes.tsx | 191 | ||||
-rw-r--r-- | src/view/com/modals/Modal.tsx | 4 | ||||
-rw-r--r-- | src/view/com/modals/Modal.web.tsx | 3 |
3 files changed, 198 insertions, 0 deletions
diff --git a/src/view/com/modals/InviteCodes.tsx b/src/view/com/modals/InviteCodes.tsx new file mode 100644 index 000000000..5e31e16a8 --- /dev/null +++ b/src/view/com/modals/InviteCodes.tsx @@ -0,0 +1,191 @@ +import React from 'react' +import {StyleSheet, TouchableOpacity, View} from 'react-native' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import Clipboard from '@react-native-clipboard/clipboard' +import {Text} from '../util/text/Text' +import {Button} from '../util/forms/Button' +import * as Toast from '../util/Toast' +import {useStores} from 'state/index' +import {ScrollView} from './util' +import {usePalette} from 'lib/hooks/usePalette' +import {isDesktopWeb} from 'platform/detection' + +export const snapPoints = ['70%'] + +export function Component({}: {}) { + const pal = usePalette('default') + const store = useStores() + + const onClose = React.useCallback(() => { + store.shell.closeModal() + }, [store]) + + if (store.me.invites.length === 0) { + return ( + <View style={[styles.container, pal.view]} testID="inviteCodesModal"> + <View style={[styles.empty, pal.viewLight]}> + <Text type="lg" style={[pal.text, styles.emptyText]}> + You don't have any invite codes yet! We'll send you some when you've + been on Bluesky for a little longer. + </Text> + </View> + <View style={styles.flex1} /> + <View style={styles.btnContainer}> + <Button + type="primary" + label="Done" + style={styles.btn} + labelStyle={styles.btnLabel} + onPress={onClose} + /> + </View> + </View> + ) + } + + return ( + <View style={[styles.container, pal.view]} testID="inviteCodesModal"> + <Text type="title-xl" style={[styles.title, pal.text]}> + Invite a Friend + </Text> + <Text type="lg" style={[styles.description, pal.text]}> + Send these invites to your friends so they can create an account. Each + code works once! + </Text> + <Text type="sm" style={[styles.description, pal.textLight]}> + ( We'll send you more periodically. ) + </Text> + <ScrollView style={[styles.scrollContainer, pal.border]}> + {store.me.invites.map((invite, i) => ( + <InviteCode + testID={`inviteCode-${i}`} + key={invite.code} + code={invite.code} + used={invite.available - invite.uses.length <= 0 || invite.disabled} + /> + ))} + </ScrollView> + <View style={styles.btnContainer}> + <Button + testID="closeBtn" + type="primary" + label="Done" + style={styles.btn} + labelStyle={styles.btnLabel} + onPress={onClose} + /> + </View> + </View> + ) +} + +function InviteCode({ + testID, + code, + used, +}: { + testID: string + code: string + used?: boolean +}) { + const pal = usePalette('default') + const [wasCopied, setWasCopied] = React.useState(false) + + const onPress = React.useCallback(() => { + Clipboard.setString(code) + Toast.show('Copied to clipboard') + setWasCopied(true) + }, [code]) + + return ( + <TouchableOpacity + testID={testID} + style={[styles.inviteCode, pal.border]} + onPress={onPress}> + <Text + testID={`${testID}-code`} + type={used ? 'md' : 'md-bold'} + style={used ? [pal.textLight, styles.strikeThrough] : pal.text}> + {code} + </Text> + {wasCopied ? ( + <Text style={pal.textLight}>Copied</Text> + ) : !used ? ( + <FontAwesomeIcon + icon={['far', 'clone']} + style={pal.text as FontAwesomeIconStyle} + /> + ) : undefined} + </TouchableOpacity> + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingBottom: isDesktopWeb ? 0 : 50, + }, + title: { + textAlign: 'center', + marginTop: 12, + marginBottom: 12, + }, + description: { + textAlign: 'center', + paddingHorizontal: 42, + marginBottom: 14, + }, + + scrollContainer: { + flex: 1, + borderTopWidth: 1, + marginTop: 4, + marginBottom: 16, + }, + + flex1: { + flex: 1, + }, + empty: { + paddingHorizontal: 20, + paddingVertical: 20, + borderRadius: 16, + marginHorizontal: 24, + marginTop: 10, + }, + emptyText: { + textAlign: 'center', + }, + + inviteCode: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderBottomWidth: 1, + paddingHorizontal: 20, + paddingVertical: 14, + }, + strikeThrough: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + + btnContainer: { + flexDirection: 'row', + justifyContent: 'center', + }, + btn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRadius: 32, + paddingHorizontal: 60, + paddingVertical: 14, + }, + btnLabel: { + fontSize: 18, + }, +}) diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 931e3fbe4..b1c7d4738 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -14,6 +14,7 @@ import * as ReportAccountModal from './ReportAccount' import * as DeleteAccountModal from './DeleteAccount' import * as ChangeHandleModal from './ChangeHandle' import * as WaitlistModal from './Waitlist' +import * as InviteCodesModal from './InviteCodes' import {usePalette} from 'lib/hooks/usePalette' import {StyleSheet} from 'react-native' @@ -73,6 +74,9 @@ export const ModalsContainer = observer(function ModalsContainer() { } else if (activeModal?.name === 'waitlist') { snapPoints = WaitlistModal.snapPoints element = <WaitlistModal.Component /> + } else if (activeModal?.name === 'invite-codes') { + snapPoints = InviteCodesModal.snapPoints + element = <InviteCodesModal.Component /> } else { return <View /> } diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 1b233cf37..e6d54926b 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -16,6 +16,7 @@ import * as RepostModal from './Repost' import * as CropImageModal from './crop-image/CropImage.web' import * as ChangeHandleModal from './ChangeHandle' import * as WaitlistModal from './Waitlist' +import * as InviteCodesModal from './InviteCodes' export const ModalsContainer = observer(function ModalsContainer() { const store = useStores() @@ -72,6 +73,8 @@ function Modal({modal}: {modal: ModalIface}) { element = <ChangeHandleModal.Component {...modal} /> } else if (modal.name === 'waitlist') { element = <WaitlistModal.Component /> + } else if (modal.name === 'invite-codes') { + element = <InviteCodesModal.Component /> } else { return null } |