Skip to content

Commit

Permalink
feat: ✨ Add option to change presence
Browse files Browse the repository at this point in the history
  • Loading branch information
SupertigerDev committed Nov 4, 2023
1 parent 1253b8f commit 5246b6a
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 34 deletions.
153 changes: 153 additions & 0 deletions src/components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React from 'react';
import {Pressable, StyleSheet, Text, View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {useCustomPortal} from '../utils/CustomPortal';
import {Modal} from './ui/Modal';
import CustomPressable from './ui/CustomPressable';
import Colors from './ui/Colors';

interface Props {
items: DropdownItem[];
selectedId: string;
title?: string;
icon?: string | JSX.Element;
}

export interface DropdownItem {
id: string | number;
label: string;
data?: any;
circleColor?: string;
onClick?: () => void;
}

export const Dropdown = (props: Props) => {
const selectedItem = props.items.find(item => item.id === props.selectedId);
const {createPortal} = useCustomPortal();

const onPress = () => {
createPortal(
close => (
<DropdownModal
title={props.title}
selectedItemId={props.selectedId}
items={props.items}
close={close}
/>
),
'dropdown-modal',
);
};

return (
<View>
{props.title && <Text style={styles.title}>{props.title}</Text>}
<CustomPressable onPress={onPress}>
<View style={styles.selectedItemContainer}>
<Item item={selectedItem!} />
<Icon name="expand-more" size={20} />
</View>
</CustomPressable>
</View>
);
};

const DropdownModal = (props: {
title?: string;
selectedItemId: string;
items: DropdownItem[];
close: () => void;
}) => {
return (
<Modal close={props.close} title={props.title}>
<View style={styles.modalContainer}>
{props.items.map(item => (
<CustomPressable
onPress={() => {
item.onClick?.();
props.close();
}}>
<View
style={[
styles.modalItemContainer,
item.id === props.selectedItemId &&
styles.modalItemSelectedContainer,
]}>
{item.id === props.selectedItemId && (
<View style={styles.selectedPill} />
)}
<Item item={item} key={item.id} />
</View>
</CustomPressable>
))}
</View>
</Modal>
);
};

const Item = (props: {item: DropdownItem}) => {
return (
<View style={styles.itemInnerContainer}>
{props.item.circleColor && (
<View
style={{...styles.circle, backgroundColor: props.item.circleColor}}
/>
)}
<Text>{props.item.label}</Text>
</View>
);
};

const styles = StyleSheet.create({
title: {
fontSize: 14,
fontWeight: 'bold',
color: Colors.primaryColor,
marginBottom: 8,
marginLeft: 8,
},
selectedItemContainer: {
flexDirection: 'row',
backgroundColor: '#0009',
borderRadius: 8,
alignItems: 'center',
padding: 8,
height: 50,
borderColor: 'rgba(255,255,255,.2)',
borderWidth: 1,
margin: 2,
},
modalContainer: {
gap: 2,
},
modalItemContainer: {
flexDirection: 'row',
borderRadius: 8,
alignItems: 'center',
padding: 12,
paddingLeft: 12,
},
modalItemSelectedContainer: {
backgroundColor: 'rgba(255,255,255,0.1)',
},
selectedPill: {
position: 'absolute',
left: 0,
backgroundColor: Colors.primaryColor,
height: 16,
width: 3,
borderRadius: 6,
marginRight: 8,
},
itemInnerContainer: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
circle: {
width: 8,
height: 8,
borderRadius: 8,
marginRight: 8,
},
});
8 changes: 7 additions & 1 deletion src/components/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,15 @@ const Tab = createBottomTabNavigator<LoggedInTabParamList>();
const TabBar = observer((props: BottomTabBarProps) => {
const {account} = useStore();

const descriptor =
props.descriptors[props.state.routes[props.state.index].key];

const hideTabBar = descriptor.options.hideTabBar;

const selectedIndex = props.state.index;
return (
<View style={styles.tabBarContainer}>
<View
style={[styles.tabBarContainer, {display: hideTabBar ? 'none' : 'flex'}]}>
<View style={styles.tabBarInnerContainer}>
<InboxTabItem {...props} selected={selectedIndex === 0} />
<FriendsTabItem {...props} selected={selectedIndex === 1} />
Expand Down
102 changes: 70 additions & 32 deletions src/components/SettingsScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useEffect} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import Avatar from './ui/Avatar';
import Header from './ui/Header';
Expand All @@ -9,6 +9,11 @@ import CustomPressable from './ui/CustomPressable';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {StackActions, useNavigation} from '@react-navigation/native';
import env from '../utils/env';
import {Dropdown, DropdownItem} from './Dropdown';
import {CustomPortalProvider} from '../utils/CustomPortal';
import {UserStatuses, userStatusDetail} from '../utils/userStatus';
import {observer} from 'mobx-react-lite';
import {updatePresence} from '../services/UserService';

export default function SettingsScreen() {
const store = useStore();
Expand All @@ -17,28 +22,76 @@ export default function SettingsScreen() {
await store.logout();
nav.getParent()?.dispatch(StackActions.replace('Splash'));
};

return (
<View style={styles.pageContainer}>
<View style={styles.pageContainerInner}>
<Header title="Settings" />
<BannerArea />
<View style={{marginTop: 60, margin: 10}}>
<SettingPressable
onPress={logoutClick}
label="Logout"
color={Colors.alertColor}
icon="logout"
/>
<SettingPressable
label={`App version: ${env.APP_VERSION || 'Unknown'}`}
icon="info"
/>
<CustomPortalProvider>
<View style={styles.pageContainer}>
<View style={styles.pageContainerInner}>
<Header title="Settings" />
<BannerArea />
<View style={{marginTop: 60, margin: 10}}>
<PresenceDropdown />
<SettingPressable
onPress={logoutClick}
label="Logout"
color={Colors.alertColor}
icon="logout"
/>
<SettingPressable
label={`App version: ${env.APP_VERSION || 'Unknown'}`}
icon="info"
/>
</View>
</View>
</View>
</View>
</CustomPortalProvider>
);
}

function BannerArea() {
const {account} = useStore();
return (
<Banner user={account.user!}>
<View style={styles.bannerContainer}>
<Avatar user={account.user!} size={80} />
<Text style={styles.usernameAndTag}>
{account.user?.username}
<Text style={styles.userTag}>:{account.user?.tag}</Text>
</Text>
</View>
</Banner>
);
}

const PresenceDropdown = observer(() => {
const {account, users} = useStore();

const user = users.get(account.user?.id!);
const status = userStatusDetail(user?.presence?.status || 0);

const dropDownItems = UserStatuses.map((item, i) => {
return {
circleColor: item.color,
id: item.id,
label: item.name === 'Offline' ? 'Appear As Offline' : item.name,
onClick: () => {
updatePresence({
status: i,
});
},
};
});

// move invisible to the bottom.
dropDownItems.push(dropDownItems.shift()!);

console.log(dropDownItems, status.id);

return (
<Dropdown title="Presence" items={dropDownItems} selectedId={status.id} />
);
});

function SettingPressable(props: {
onPress?(): void;
label: string;
Expand All @@ -55,21 +108,6 @@ function SettingPressable(props: {
);
}

function BannerArea() {
const {account} = useStore();
return (
<Banner user={account.user!}>
<View style={styles.bannerContainer}>
<Avatar user={account.user!} size={80} />
<Text style={styles.usernameAndTag}>
{account.user?.username}
<Text style={styles.userTag}>:{account.user?.tag}</Text>
</Text>
</View>
</Banner>
);
}

const styles = StyleSheet.create({
pageContainer: {
backgroundColor: Colors.backgroundColor,
Expand Down
9 changes: 8 additions & 1 deletion src/components/ui/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useLayoutEffect} from 'react';
import {
Pressable,
ScrollView,
Expand Down Expand Up @@ -34,6 +34,13 @@ export function Modal(props: ModalProps) {
[navigation],
);

useLayoutEffect(() => {
navigation.setOptions({hideTabBar: true});
return () => {
navigation.setOptions({hideTabBar: false});
};
}, [navigation]);

return (
<Pressable style={styles.backdrop} onPress={props.close}>
<ScrollView
Expand Down
10 changes: 10 additions & 0 deletions src/services/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
RawPost,
RawUser,
} from '../store/RawData';
import {Presence} from '../store/users';
import env from '../utils/env';
import {request} from './Request';
import ServiceEndpoints from './ServiceEndpoints';
Expand All @@ -23,6 +24,15 @@ export async function loginRequest(
});
}

export async function updatePresence(presence: Partial<Presence>) {
return request<RawInboxWithoutChannel & {channel: RawChannel}>({
url: env.SERVER_URL + '/api' + ServiceEndpoints.updatePresence(),
method: 'POST',
body: presence,
useToken: true,
});
}

export async function openDMChannelRequest(userId: string) {
return request<RawInboxWithoutChannel & {channel: RawChannel}>({
url: env.SERVER_URL + '/api' + ServiceEndpoints.openUserDM(userId),
Expand Down

0 comments on commit 5246b6a

Please sign in to comment.