diff --git a/package.json b/package.json
index b8dcf0c..7f4ef8b 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@notifee/react-native": "^7.8.0",
"@react-native-async-storage/async-storage": "^1.19.3",
"@react-native-clipboard/clipboard": "^1.11.2",
+ "@react-native-community/hooks": "^3.0.0",
"@react-native/eslint-config": "^0.73.0",
"@react-native/metro-config": "^0.73.0",
"@tradle/react-native-http": "^2.0.1",
diff --git a/src/Generic.tsx b/src/Generic.tsx
index 864f4d2..30d1346 100644
--- a/src/Generic.tsx
+++ b/src/Generic.tsx
@@ -7,7 +7,7 @@ import FastImage from 'react-native-fast-image';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
-import {API, Channel, Client, Server, User} from 'revolt.js';
+import {API, Channel, Client, Server} from 'revolt.js';
import {currentTheme, setTheme, themes, styles} from './Theme';
import {Button, Text} from './components/common/atoms';
@@ -310,10 +310,10 @@ export const app = {
pushToQueue: m => {},
joinInvite: async (i: API.InviteResponse) => {},
logOut: () => {},
- openMemberList: (c: Channel | Server | null, u: User[] | null) => {},
+ openMemberList: (data: Channel | Server | null) => {},
openChannelContextMenu: (c: Channel | null) => {},
openStatusMenu: (state: boolean) => {},
- openReportMenu: (object: ReportedObject| null) => {},
+ openReportMenu: (object: ReportedObject | null) => {},
};
export function setFunction(name: string, func: any) {
diff --git a/src/Modals.tsx b/src/Modals.tsx
index f68379e..4eb1027 100644
--- a/src/Modals.tsx
+++ b/src/Modals.tsx
@@ -1,16 +1,14 @@
import React from 'react';
-import {Modal, Pressable, ScrollView, View} from 'react-native';
+import {Modal, Pressable, View} from 'react-native';
import {observer} from 'mobx-react-lite';
-import BottomSheet from '@gorhom/bottom-sheet';
-import Modal2 from 'react-native-modal';
import ImageViewer from 'react-native-image-zoom-viewer';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
-import {API, Channel, Message, Server, User} from 'revolt.js';
+import {API, Channel, User} from 'revolt.js';
import {app, client, openUrl, setFunction} from './Generic';
-import {styles, currentTheme} from './Theme';
+import {currentTheme} from './Theme';
import {GapView} from './components/layout';
import {
BotInviteSheet,
@@ -24,88 +22,12 @@ import {
SettingsSheet,
StatusSheet,
} from './components/sheets/';
-import type {ReportedObject} from './lib/types';
-
-const MBottomSheet = observer(
- ({
- sheetKey,
- visible,
- callback,
- includeScrollView,
- children,
- }: {
- sheetKey: string;
- visible: boolean;
- callback: Function;
- includeScrollView?: boolean;
- children: any;
- }) => {
- return (
- callback()}
- onBackButtonPress={() => callback()}
- swipeDirection={'down'}
- onSwipeComplete={() => callback()}
- propagateSwipe
- style={{
- width: '100%',
- marginHorizontal: 0,
- marginBottom: 0,
- justifyContent: 'flex-end',
- }}>
-
-
- {includeScrollView ? {children} : children}
-
-
- );
- },
-);
export const Modals = observer(() => {
- const [messageMenuState, setMessageMenuState] = React.useState({
- showMessageMenu: false,
- contextMenuMessage: null,
- } as {showMessageMenu: boolean; contextMenuMessage: Message | null});
- const [statusMenuState, setStatusMenuState] = React.useState(false);
- const [profileMenuState, setProfileMenuState] = React.useState({
- showUserMenu: false,
- contextMenuUser: null,
- contextMenuUserServer: null,
- } as {
- showUserMenu: boolean;
- contextMenuUser: User | null;
- contextMenuUserServer: Server | null;
- });
const [imageViewerState, setImageViewerState] = React.useState({
i: null as any,
});
const [settingsVisibility, setSettingsVisibility] = React.useState(false);
- const [reportMenuState, setReportMenuState] = React.useState({
- showReportMenu: false,
- reportObject: null,
- } as {
- showReportMenu: boolean;
- reportObject: ReportedObject | null;
- });
- const [channelMenuState, setChannelMenuState] = React.useState({
- showChannelMenu: false,
- channelMenuChannel: null,
- } as {
- showChannelMenu: boolean;
- channelMenuChannel: Channel | null;
- });
const [inviteServer, setInviteServer] = React.useState({
inviteServer: null,
inviteServerCode: '',
@@ -114,49 +36,9 @@ export const Modals = observer(() => {
inviteServerCode: string;
});
const [inviteBot, setInviteBot] = React.useState(null as User | null);
- const [serverMenuState, setServerMenuState] = React.useState({
- showServerMenu: false,
- contextMenuServer: null,
- } as {
- showServerMenu: boolean;
- contextMenuServer: Server | null;
- });
- const [memberListState, setMemberListState] = React.useState({
- showMemberList: false,
- memberListContext: null,
- memberListUsers: null,
- } as {
- showMemberList: boolean;
- memberListContext: Channel | /* Server | */ null;
- memberListUsers: User[] | null;
- });
- setFunction('openMessage', async (m: Message | null) => {
- setMessageMenuState(
- m
- ? {showMessageMenu: true, contextMenuMessage: m}
- : {...messageMenuState, showMessageMenu: false},
- );
- });
- setFunction('openStatusMenu', async (show: boolean) => {
- setStatusMenuState(show);
- });
- setFunction('openProfile', async (u: User | null, s: Server | null) => {
- setProfileMenuState(
- u
- ? {
- showUserMenu: true,
- contextMenuUser: u,
- contextMenuUserServer: s,
- }
- : {...profileMenuState, showUserMenu: false},
- );
- });
setFunction('openDirectMessage', async (dm: Channel) => {
- setProfileMenuState({
- ...profileMenuState,
- showUserMenu: false,
- });
+ app.openProfile(null);
app.openChannel(dm);
});
setFunction('openImage', async (a: any) => {
@@ -165,20 +47,6 @@ export const Modals = observer(() => {
setFunction('openSettings', async (o: boolean) => {
setSettingsVisibility(o);
});
- setFunction('openReportMenu', async (object: ReportedObject | null) => {
- setReportMenuState(
- object
- ? {showReportMenu: true, reportObject: object}
- : {...reportMenuState, showReportMenu: false},
- );
- });
- setFunction('openChannelContextMenu', async (channel: Channel | null) => {
- setChannelMenuState(
- channel
- ? {showChannelMenu: true, channelMenuChannel: channel}
- : {...channelMenuState, showChannelMenu: false},
- );
- });
setFunction('openInvite', async (i: string) => {
try {
let community = await client.fetchInvite(i);
@@ -195,59 +63,16 @@ export const Modals = observer(() => {
setFunction('openBotInvite', async (id: string) => {
setInviteBot(await client.bots.fetchPublic(id).catch(e => e));
});
- setFunction('openServerContextMenu', async (s: Server | null) => {
- setServerMenuState(
- s
- ? {showServerMenu: true, contextMenuServer: s}
- : {...serverMenuState, showServerMenu: false},
- );
- });
- setFunction(
- 'openMemberList',
- async (context: Channel | /* Server | */ null, users: User[] | null) => {
- setMemberListState(
- context
- ? {
- showMemberList: true,
- memberListContext: context,
- memberListUsers: users,
- }
- : {...memberListState, showMemberList: false},
- );
- },
- );
return (
<>
- app.openMessage(null)}>
- {
- app.openMessage(null);
- }}
- message={messageMenuState.contextMenuMessage!}
- />
-
- {
- app.openStatusMenu(false);
- }}>
-
-
- app.openProfile(null)}
- includeScrollView>
-
-
+
+
+
+
+
+
+
{
onRequestClose={() => setSettingsVisibility(false)}>
setSettingsVisibility(false)} />
- app.openReportMenu(null)}>
-
-
- app.openChannelContextMenu(null)}>
-
-
{
bot={inviteBot!}
/>
- app.openServerContextMenu(null)}>
- {
- app.openServerContextMenu(null);
- }}
- server={serverMenuState.contextMenuServer!}
- />
-
- app.openMemberList(null, null)}>
-
-
>
);
});
diff --git a/src/components/common/BottomSheet.tsx b/src/components/common/BottomSheet.tsx
new file mode 100644
index 0000000..b4cc3b8
--- /dev/null
+++ b/src/components/common/BottomSheet.tsx
@@ -0,0 +1,43 @@
+import React, {useMemo} from 'react';
+import {StyleSheet} from 'react-native';
+
+import BottomSheetCore, {
+ BottomSheetBackdrop,
+ BottomSheetScrollView,
+} from '@gorhom/bottom-sheet';
+import {observer} from 'mobx-react-lite';
+
+import {currentTheme} from '../../Theme';
+
+export const BottomSheet = observer(
+ ({sheetRef, children}: {sheetRef: any; children: any}) => {
+ const snapPoints = useMemo(() => ['50%', '70%', '90%'], []);
+
+ return (
+
+ {children}
+
+ );
+ },
+);
+
+const localStyles = StyleSheet.create({
+ sheetBackground: {
+ backgroundColor: currentTheme.backgroundSecondary,
+ },
+ handleIndicator: {
+ backgroundColor: currentTheme.foregroundPrimary,
+ width: '25%',
+ padding: 3,
+ marginVertical: 8,
+ },
+});
diff --git a/src/components/sheets/ChannelInfoSheet.tsx b/src/components/sheets/ChannelInfoSheet.tsx
index 45fbadd..d70ae0c 100644
--- a/src/components/sheets/ChannelInfoSheet.tsx
+++ b/src/components/sheets/ChannelInfoSheet.tsx
@@ -1,18 +1,43 @@
-import React from 'react';
-import {ScrollView, View} from 'react-native';
+import React, {useRef} from 'react';
+import {View} from 'react-native';
import {observer} from 'mobx-react-lite';
+import BottomSheetCore from '@gorhom/bottom-sheet';
+import {useBackHandler} from '@react-native-community/hooks';
+
import {Channel, User} from 'revolt.js';
+import {setFunction} from '../../Generic';
import {currentTheme} from '../../Theme';
import {Text} from '../common/atoms';
+import {BottomSheet} from '../common/BottomSheet';
import {MarkdownView} from '../common/MarkdownView';
-export const ChannelInfoSheet = observer(({channel}: {channel: Channel}) => {
+export const ChannelInfoSheet = observer(() => {
+ const [channel, setChannel] = React.useState(null as Channel | null);
const [groupMembers, setGroupMembers] = React.useState([] as User[]);
+ const sheetRef = useRef(null);
+
+ useBackHandler(() => {
+ if (channel) {
+ sheetRef.current?.close();
+ return true;
+ }
+
+ return false;
+ });
+
+ setFunction('openChannelContextMenu', async (c: Channel | null) => {
+ setChannel(c);
+ c ? sheetRef.current?.expand() : sheetRef.current?.close();
+ });
+
React.useEffect(() => {
async function fetchMembers() {
+ if (!channel) {
+ return;
+ }
const m =
channel.channel_type === 'Group' ? await channel.fetchMembers() : [];
setGroupMembers(m);
@@ -20,38 +45,46 @@ export const ChannelInfoSheet = observer(({channel}: {channel: Channel}) => {
fetchMembers();
}, [channel]);
return (
-
-
- {channel.name}
-
- {channel.channel_type === 'Group'
- ? `Group (${groupMembers.length} ${
- groupMembers.length === 1 ? 'member' : 'members'
- })`
- : 'Regular channel'}
-
- {channel.description ? (
-
-
- {channel.description}
-
-
- ) : null}
+
+
+ {!channel ? (
+ <>>
+ ) : (
+ <>
+
+ {channel.name}
+
+ {channel.channel_type === 'Group'
+ ? `Group (${groupMembers.length} ${
+ groupMembers.length === 1 ? 'member' : 'members'
+ })`
+ : 'Regular channel'}
+
+ {channel.description ? (
+
+
+ {channel.description}
+
+
+ ) : null}
+
+ >
+ )}
-
+
);
});
diff --git a/src/components/sheets/MemberListSheet.tsx b/src/components/sheets/MemberListSheet.tsx
index b46c40c..1547b93 100644
--- a/src/components/sheets/MemberListSheet.tsx
+++ b/src/components/sheets/MemberListSheet.tsx
@@ -1,30 +1,68 @@
-import React from 'react';
-import {ScrollView, View} from 'react-native';
+import React, {useEffect, useRef} from 'react';
+import {View} from 'react-native';
import {observer} from 'mobx-react-lite';
-import {Channel, Server, User} from 'revolt.js'; // TODO: add Member support
+import BottomSheetCore from '@gorhom/bottom-sheet';
+import {useBackHandler} from '@react-native-community/hooks';
+import {Channel, Server, User} from 'revolt.js';
+
+import {setFunction} from '../../Generic';
import {Text} from '../common/atoms';
+import {BottomSheet} from '../common/BottomSheet';
import {UserList} from '../navigation/UserList';
-interface ServerMemberList {
- context: Server;
- users: User[]; // Member[];
-}
-
-interface ChannelMemberList {
- context: Channel;
- users: User[];
-}
-
-export const MemberListSheet = observer(
- ({context, users}: ServerMemberList | ChannelMemberList) => {
- return (
-
- {context.name ?? context._id} members
-
-
-
- );
- },
-);
+export const MemberListSheet = observer(() => {
+ const [context, setContext] = React.useState(null as Channel | Server | null);
+ const [users, setUsers] = React.useState([] as User[]);
+
+ const sheetRef = useRef(null);
+
+ useBackHandler(() => {
+ if (context) {
+ sheetRef.current?.close();
+ return true;
+ }
+
+ return false;
+ });
+
+ setFunction('openMemberList', async (ctx: Channel | Server | null) => {
+ if (ctx !== context) {
+ setUsers([]);
+ }
+ setContext(ctx);
+ ctx ? sheetRef.current?.expand() : sheetRef.current?.close();
+ });
+
+ useEffect(() => {
+ async function getUsers() {
+ if (!context) {
+ return;
+ }
+ const u =
+ context instanceof Server
+ ? (await context.fetchMembers()).users
+ : await context.fetchMembers();
+
+ setUsers(u);
+ }
+ getUsers();
+ }, [context]);
+
+ return (
+
+
+ {!context ? (
+ <>>
+ ) : (
+ <>
+ {context.name ?? context._id} members
+
+
+ >
+ )}
+
+
+ );
+});
diff --git a/src/components/sheets/MessageMenuSheet.tsx b/src/components/sheets/MessageMenuSheet.tsx
index dacd5cf..ede614e 100644
--- a/src/components/sheets/MessageMenuSheet.tsx
+++ b/src/components/sheets/MessageMenuSheet.tsx
@@ -1,141 +1,171 @@
-import React from 'react';
-import {ScrollView, View} from 'react-native';
+import React, {useRef} from 'react';
+import {View} from 'react-native';
import {observer} from 'mobx-react-lite';
+import BottomSheetCore from '@gorhom/bottom-sheet';
import Clipboard from '@react-native-clipboard/clipboard';
+import {useBackHandler} from '@react-native-community/hooks';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Message} from 'revolt.js';
-import {app} from '../../Generic';
+import {app, setFunction} from '../../Generic';
import {currentTheme, styles} from '../../Theme';
import {ContextButton, CopyIDButton, Text} from '../common/atoms';
+import {BottomSheet} from '../common/BottomSheet';
import {ReplyMessage} from '../common/messaging';
-export const MessageMenuSheet = observer(
- ({setState, message}: {setState: Function; message: Message}) => {
- return (
- <>
-
-
-
- {message?.channel?.havePermission('SendMessage') ? (
- {
- let replyingMessages = [...app.getReplyingMessages()];
- if (
- replyingMessages.filter(m => m.message._id === message._id)
- .length > 0
- ) {
- return;
- }
- if (replyingMessages.length >= 5) {
- return;
- }
- if (app.getEditingMessage()) {
- return;
- }
- replyingMessages.push({
- message: message,
- mentions: false,
- });
- app.setReplyingMessages(replyingMessages);
- setState();
- }}>
-
-
-
- Reply
-
- ) : null}
- {message.content ? (
- {
- Clipboard.setString(message.content!);
- }}>
-
-
-
- Copy content
-
- ) : null}
- {app.settings.get('ui.showDeveloperFeatures') ? (
-
- ) : null}
- {
- Clipboard.setString(message.url);
- }}>
-
-
-
- Copy message link
-
- {message?.author?.relationship === 'User' ? (
- {
- app.setMessageBoxInput(message?.content);
- app.setEditingMessage(message);
- app.setReplyingMessages([]);
- setState();
- }}>
-
-
-
- Edit
-
- ) : null}
- {message?.channel?.havePermission('ManageMessages') ||
- message?.author?.relationship === 'User' ? (
- {
- message.delete();
- setState();
- }}>
-
-
-
- Delete
-
- ) : null}
- {message?.author?.relationship !== 'User' ? (
- {
- app.openReportMenu({object: message, type: 'Message'});
- setState();
- }}>
-
-
+export const MessageMenuSheet = observer(() => {
+ const [message, setMessage] = React.useState(null as Message | null);
+
+ const sheetRef = useRef(null);
+
+ useBackHandler(() => {
+ if (message) {
+ sheetRef.current?.close();
+ return true;
+ }
+
+ return false;
+ });
+
+ setFunction('openMessage', async (m: Message | null) => {
+ setMessage(m);
+ m ? sheetRef.current?.expand() : sheetRef.current?.close();
+ });
+ return (
+
+
+ {!message ? (
+ <>>
+ ) : (
+ <>
+
+
- Report Message
-
- ) : null}
-
- >
- );
- },
-);
+ {message?.channel?.havePermission('SendMessage') ? (
+ {
+ let replyingMessages = [...app.getReplyingMessages()];
+ if (
+ replyingMessages.filter(m => m.message._id === message._id)
+ .length > 0
+ ) {
+ return;
+ }
+ if (replyingMessages.length >= 5) {
+ return;
+ }
+ if (app.getEditingMessage()) {
+ return;
+ }
+ replyingMessages.push({
+ message: message,
+ mentions: false,
+ });
+ app.setReplyingMessages(replyingMessages);
+ app.openMessage(null);
+ }}>
+
+
+
+ Reply
+
+ ) : null}
+ {message.content ? (
+ {
+ Clipboard.setString(message.content!);
+ }}>
+
+
+
+ Copy content
+
+ ) : null}
+ {app.settings.get('ui.showDeveloperFeatures') ? (
+
+ ) : null}
+ {
+ Clipboard.setString(message.url);
+ }}>
+
+
+
+ Copy message link
+
+ {message?.author?.relationship === 'User' ? (
+ {
+ app.setMessageBoxInput(message?.content);
+ app.setEditingMessage(message);
+ app.setReplyingMessages([]);
+ app.openMessage(null);
+ }}>
+
+
+
+ Edit
+
+ ) : null}
+ {message?.channel?.havePermission('ManageMessages') ||
+ message?.author?.relationship === 'User' ? (
+ {
+ message.delete();
+ app.openMessage(null);
+ }}>
+
+
+
+ Delete
+
+ ) : null}
+ {message?.author?.relationship !== 'User' ? (
+ {
+ app.openReportMenu({object: message, type: 'Message'});
+ app.openMessage(null);
+ }}>
+
+
+
+ Report Message
+
+ ) : null}
+
+ >
+ )}
+
+
+ );
+});
diff --git a/src/components/sheets/ProfileSheet.tsx b/src/components/sheets/ProfileSheet.tsx
index b7a3f94..2409d57 100644
--- a/src/components/sheets/ProfileSheet.tsx
+++ b/src/components/sheets/ProfileSheet.tsx
@@ -1,8 +1,10 @@
/* eslint-disable no-bitwise */
-import React, {useEffect} from 'react';
-import {ScrollView, TouchableOpacity, View} from 'react-native';
+import React, {useEffect, useRef} from 'react';
+import {Pressable, ScrollView, TouchableOpacity, View} from 'react-native';
import {observer} from 'mobx-react-lite';
+import BottomSheetCore from '@gorhom/bottom-sheet';
+import {useBackHandler} from '@react-native-community/hooks';
// import FastImage from 'react-native-fast-image';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
@@ -10,624 +12,700 @@ import FA5Icon from 'react-native-vector-icons/FontAwesome5';
import {User, Server} from 'revolt.js';
-import {app, client, GeneralAvatar, openUrl} from '../../Generic';
+import {app, client, GeneralAvatar, openUrl, setFunction} from '../../Generic';
import {BADGES, USER_IDS} from '../../lib/consts';
import {parseRevoltNodes, showToast} from '../../lib/utils';
import {Avatar, MiniProfile, RoleView} from '../../Profile';
-import {currentTheme} from '../../Theme';
-import {Button, ContextButton, Link, Text, Username} from '../common/atoms';
+import {currentTheme, styles} from '../../Theme';
+import {
+ Button,
+ ContextButton,
+ CopyIDButton,
+ Link,
+ Text,
+ Username,
+} from '../common/atoms';
+import {BottomSheet} from '../common/BottomSheet';
import {MarkdownView} from '../common/MarkdownView';
-import {UserMenuSheet} from './index';
import {UserList} from '../navigation/UserList';
// const Image = FastImage;
-export const ProfileSheet = observer(
- ({user, server}: {user: User; server?: Server}) => {
- const [section, setSection] = React.useState('Profile');
- const [profile, setProfile] = React.useState(
- {} as {content?: string | null | undefined},
- );
- const [mutual, setMutual] = React.useState(
- {} as {users: User[]; servers: Server[]},
- );
- const [showMenu, setShowMenu] = React.useState(false);
+export const ProfileSheet = observer(() => {
+ const [user, setUser] = React.useState(null as User | null);
+ const [server, setServer] = React.useState(null as Server | null);
- useEffect(() => {
- async function getInfo() {
- const p = await user.fetchProfile();
- const rawMutuals =
- user.relationship !== 'User'
- ? await user.fetchMutual()
- : {users: [] as string[], servers: [] as string[]};
+ const sheetRef = useRef(null);
- const fetchedMutualUsers: User[] = [];
- for (const u of rawMutuals.users) {
- fetchedMutualUsers.push(await client.users.fetch(u));
- }
+ useBackHandler(() => {
+ if (user) {
+ sheetRef.current?.close();
+ return true;
+ }
- const fetchedMutualServers: Server[] = [];
- for (const s of rawMutuals.servers) {
- fetchedMutualServers.push(await client.servers.fetch(s));
- }
+ return false;
+ });
- const m = {servers: fetchedMutualServers, users: fetchedMutualUsers};
+ setFunction('openProfile', async (u: User | null, s: Server | null) => {
+ if (u !== user) {
+ setProfile({});
+ setMutual({users: [], servers: []});
+ setUser(u);
+ }
+ setServer(s);
+ u ? sheetRef.current?.expand() : sheetRef.current?.close();
+ });
- setProfile(p);
- setMutual(m);
+ const [section, setSection] = React.useState('Profile');
+ const [profile, setProfile] = React.useState(
+ {} as {content?: string | null | undefined},
+ );
+ const [mutual, setMutual] = React.useState(
+ {} as {users: User[]; servers: Server[]},
+ );
+ const [showMenu, setShowMenu] = React.useState(false);
+
+ useEffect(() => {
+ async function getInfo() {
+ if (!user) {
+ return;
+ }
+ const p = await user.fetchProfile();
+ const rawMutuals =
+ user.relationship !== 'User'
+ ? await user.fetchMutual()
+ : {users: [] as string[], servers: [] as string[]};
+
+ const fetchedMutualUsers: User[] = [];
+ for (const u of rawMutuals.users) {
+ fetchedMutualUsers.push(await client.users.fetch(u));
+ }
+
+ const fetchedMutualServers: Server[] = [];
+ for (const s of rawMutuals.servers) {
+ fetchedMutualServers.push(await client.servers.fetch(s));
}
- getInfo();
- }, [user]);
- return showMenu ? (
-
- ) : (
- <>
-
-
-
- setShowMenu(true)}>
+
+ const m = {servers: fetchedMutualServers, users: fetchedMutualUsers};
+
+ setProfile(p);
+ setMutual(m);
+ }
+ getInfo();
+ }, [user]);
+
+ return (
+
+
+ {!user ? (
+ <>>
+ ) : showMenu ? (
+ <>
+ {
+ setShowMenu(false);
+ }}>
-
-
-
-
-
-
-
- {server ? (
- <>
- {client.members.getKey({
- server: server?._id,
- user: user._id,
- })?.avatar?._id !== user.avatar?._id ? (
- <>
-
-
- @
-
- >
- ) : (
- @
- )}
-
- >
- ) : null}
-
- {user.status?.text ? {user.status?.text} : <>>}
-
-
- {user.flags ? (
- user.flags & 1 ? (
- User is suspended
- ) : user.flags & 2 ? (
- User deleted their account
- ) : user.flags & 4 ? (
- User is banned
- ) : null
- ) : null}
- {user.relationship !== 'User' ? (
+
+ Return to Profile
+
+
+ {app.settings.get('ui.showDeveloperFeatures') ? (
+
+ ) : null}
+ {user.relationship !== 'User' ? (
+ {
+ app.openReportMenu({object: user, type: 'User'});
+ setShowMenu(false);
+ app.openProfile(null);
+ }}>
+
+
+
+ Report User
+
+ ) : null}
+ >
+ ) : (
<>
-
+
+
- {!user.bot ? (
- user.relationship === 'Friend' ? (
-
- ) : user.relationship === 'Incoming' ? (
+ setShowMenu(true)}>
+
+
+
+
+
+
+
+
+ {server ? (
<>
-
-
+ {client.members.getKey({
+ server: server?._id,
+ user: user._id,
+ })?.avatar?._id !== user.avatar?._id ? (
+ <>
+
+
+ @
+
+ >
+ ) : (
+ @
+ )}
+
>
- ) : user.relationship === 'Outgoing' ? (
-
- ) : user.relationship !== 'Blocked' &&
- user.relationship !== 'BlockedOther' ? (
-
- ) : null
- ) : (
- <>>
- )}
+ ) : null}
+
+ {user.status?.text ? {user.status?.text} : <>>}
-
-
-
-
-
-
-
- >
- ) : null}
- {section === 'Profile' ? (
-
- {user.relationship === 'User' ? (
- <>
- STATUS
-
- Status settings have moved.
+ {user.flags ? (
+ user.flags & 1 ? (
+ User is suspended
+ ) : user.flags & 2 ? (
+
+ User deleted their account
+ ) : user.flags & 4 ? (
+ User is banned
+ ) : null
+ ) : null}
+ {user.relationship !== 'User' ? (
+ <>
- {
- app.openStatusMenu(true);
}}>
-
- Open status menu
-
-
+ {!user.bot ? (
+ user.relationship === 'Friend' ? (
+
+ ) : user.relationship === 'Incoming' ? (
+ <>
+
+
+ >
+ ) : user.relationship === 'Outgoing' ? (
+
+ ) : user.relationship !== 'Blocked' &&
+ user.relationship !== 'BlockedOther' ? (
+
+ ) : null
+ ) : (
+ <>>
+ )}
+
- >
- ) : user.bot ? (
- <>
- BOT OWNER
- {user.bot.owner && client.users.get(user.bot.owner) ? (
+
- ) : (
-
- Unloaded user
-
- )}
- >
- ) : null}
- {server && }
- {user.badges ? (
- <>
+
+
- BADGES {'('}
-
- {')'}
+
-
+ >
+ ) : null}
+ {section === 'Profile' ? (
+
+ {user.bot ? (
<>
- {Object.keys(BADGES).map(b => {
- if (user.badges! & BADGES[b]) {
- return (
-
- {(() => {
- switch (b) {
- case 'Founder':
- return (
- showToast('Founder')}>
-
-
- );
- case 'Developer':
- return (
-
- showToast('Revolt Developer')
- }>
-
-
- );
- case 'Translator':
- return (
- showToast('Translator')}>
-
-
- );
- case 'Supporter':
- return (
- showToast('Donator')}
- onLongPress={() =>
- openUrl('https://insrt.uk/donate')
- }>
-
-
- );
- case 'ResponsibleDisclosure':
- return (
-
- showToast(
- 'Responisbly disclosed a security issue',
- )
- }>
-
-
- );
- case 'EarlyAdopter':
- return (
-
- showToast('Early Adopter')
- }>
-
-
- );
- case 'PlatformModeration':
- return (
-
- showToast('Platform Moderator')
- }>
-
-
- );
- case 'Paw':
- return (
- showToast("Insert's Paw")}>
- ✌️
-
- );
- case 'ReservedRelevantJokeBadge1':
- return (
- showToast('amogus')}>
- 📮
-
- );
- case 'ReservedRelevantJokeBadge2':
- return (
-
- showToast("It's Morbin Time")
- }>
- 🦇
-
- );
- default:
- return (
- showToast(b)}>
-
- [{b}]
-
-
- );
- }
- })()}
-
- );
- }
- })}
- {USER_IDS.developers.includes(user._id) ? (
- showToast('RVMob Developer')}>
-
-
- RV
-
-
-
- ) : null}
- {user._id === USER_IDS.teamMembers.lea ? (
- showToast("Lea's Paw")}>
-
- BOT OWNER
+ {user.bot.owner && client.users.get(user.bot.owner) ? (
+
- ) : null}
- {user._id === USER_IDS.teamMembers.insert ? (
- showToast('raccoon 🦝')}>
-
- 🦝
- {/*
+ ) : (
+
+ Unloaded user
+
+ )}
+ >
+ ) : null}
+ {server && }
+ {user.badges ? (
+ <>
+
+ BADGES {'('}
+
+ {')'}
+
+
+ <>
+ {Object.keys(BADGES).map(b => {
+ if (user.badges! & BADGES[b]) {
+ return (
+
+ {(() => {
+ switch (b) {
+ case 'Founder':
+ return (
+ showToast('Founder')}>
+
+
+ );
+ case 'Developer':
+ return (
+
+ showToast('Revolt Developer')
+ }>
+
+
+ );
+ case 'Translator':
+ return (
+
+ showToast('Translator')
+ }>
+
+
+ );
+ case 'Supporter':
+ return (
+ showToast('Donator')}
+ onLongPress={() =>
+ openUrl('https://insrt.uk/donate')
+ }>
+
+
+ );
+ case 'ResponsibleDisclosure':
+ return (
+
+ showToast(
+ 'Responisbly disclosed a security issue',
+ )
+ }>
+
+
+ );
+ case 'EarlyAdopter':
+ return (
+
+ showToast('Early Adopter')
+ }>
+
+
+ );
+ case 'PlatformModeration':
+ return (
+
+ showToast('Platform Moderator')
+ }>
+
+
+ );
+ case 'Paw':
+ return (
+
+ showToast("Insert's Paw")
+ }>
+ ✌️
+
+ );
+ case 'ReservedRelevantJokeBadge1':
+ return (
+ showToast('amogus')}>
+ 📮
+
+ );
+ case 'ReservedRelevantJokeBadge2':
+ return (
+
+ showToast("It's Morbin Time")
+ }>
+ 🦇
+
+ );
+ default:
+ return (
+ showToast(b)}>
+
+ [{b}]
+
+
+ );
+ }
+ })()}
+
+ );
+ }
+ })}
+ {USER_IDS.developers.includes(user._id) ? (
+ showToast('RVMob Developer')}>
+
+
+ RV
+
+
+
+ ) : null}
+ {user._id === USER_IDS.teamMembers.lea ? (
+ showToast("Lea's Paw")}>
+
+
+
+
+ ) : null}
+ {user._id === USER_IDS.teamMembers.insert ? (
+ showToast('raccoon 🦝')}>
+
+ 🦝
+ {/* */}
-
-
- ) : null}
- {user._id === USER_IDS.teamMembers.infi ? (
- showToast('ink-fi')}>
-
- 🐙
-
-
- ) : null}
+
+
+ ) : null}
+ {user._id === USER_IDS.teamMembers.infi ? (
+ showToast('ink-fi')}>
+
+ 🐙
+
+
+ ) : null}
+ >
+
>
-
- >
- ) : null}
- BIO
- {profile.content ? (
- {parseRevoltNodes(profile.content)}
+ ) : null}
+ BIO
+ {profile.content ? (
+
+ {parseRevoltNodes(profile.content)}
+
+ ) : null}
+
+
+ ) : section === 'Mutual Servers' ? (
+
+ MUTUAL SERVERS
+ {mutual.servers?.map(srv => {
+ return (
+ {
+ app.openServer(srv);
+ app.openProfile(null);
+ app.openLeftMenu(true);
+ }}>
+
+
+ {srv!.name}
+
+
+ );
+ })}
+
+
+ ) : section === 'Mutual Friends' ? (
+
+ MUTUAL FRIENDS
+
+
+
) : null}
-
-
- ) : section === 'Mutual Servers' ? (
-
- MUTUAL SERVERS
- {mutual.servers?.map(srv => {
- return (
- {
- app.openServer(srv);
- app.openProfile(null);
- app.openLeftMenu(true);
- }}>
-
-
- {srv!.name}
-
-
- );
- })}
-
-
- ) : section === 'Mutual Friends' ? (
-
- MUTUAL FRIENDS
-
-
-
- ) : null}
- >
- );
- },
-);
+ >
+ )}
+
+
+ );
+});
diff --git a/src/components/sheets/ReportSheet.tsx b/src/components/sheets/ReportSheet.tsx
index 3c0a270..19988f7 100644
--- a/src/components/sheets/ReportSheet.tsx
+++ b/src/components/sheets/ReportSheet.tsx
@@ -1,16 +1,20 @@
-import React from 'react';
+import React, {useMemo, useRef} from 'react';
import {ScrollView, TextInput, View} from 'react-native';
import {observer} from 'mobx-react-lite';
+import BottomSheetCore from '@gorhom/bottom-sheet';
+import {useBackHandler} from '@react-native-community/hooks';
+
import {Message} from 'revolt.js';
-import {client} from '../../Generic';
+import {app, client, setFunction} from '../../Generic';
import {USER_IDS} from '../../lib/consts';
import type {ReportedObject} from '../../lib/types';
import {Avatar} from '../../Profile';
import {currentTheme} from '../../Theme';
import {Button, Text, Username} from '../common/atoms';
import {MarkdownView} from '../common/MarkdownView';
+import {BottomSheet} from '../common/BottomSheet';
type Reason = {
label: string;
@@ -60,307 +64,378 @@ const inputStyles = {
color: currentTheme.foregroundPrimary,
};
-export const ReportModal = observer(({obj}: {obj: ReportedObject}) => {
- const [additionalContext, setAdditionalContext] = React.useState('');
- const [reason, setReason] = React.useState({} as Reason);
- const [status, setStatus] = React.useState({} as Status);
- const type = obj.type;
- const object = obj.object;
- const messageReasons = [
- {label: 'This message breaks one or more laws', reason: 'Illegal'},
- {label: 'This message promotes harm', reason: 'PromotesHarm'},
- {
- label: 'This message is spam or abuse of the platform',
- reason: 'SpamAbuse',
- },
- {
- label: 'This message contains malware or dangerous links',
- reason: 'Malware',
- },
- {
- label: 'This message is harassing me or someone else',
- reason: 'Harassment',
- },
- {label: 'Something else not listed here', reason: 'NoneSpecified'},
- ] as Reason[];
- const serverReasons = [
- {label: 'This server breaks one or more laws', reason: 'Illegal'},
- {label: 'This server promotes harm', reason: 'PromotesHarm'},
- {
- label: 'This server is spamming or abusing the platform',
- reason: 'SpamAbuse',
- },
- {
- label: 'This server contains malware or dangerous links',
- reason: 'Malware',
- },
- {
- label: 'This server is harassing me or someone else',
- reason: 'Harassment',
- },
- {label: 'Something else not listed here', reason: 'NoneSpecified'},
- ] as Reason[];
- const userReasons = [
- {
- label: "This user's profile is inappropriate for a general audience",
- reason: 'InappropriateProfile',
- },
- {
- label: 'This user is spamming or abusing the platform',
- reason: 'SpamAbuse',
- },
- {
- label: 'This user is impersonating me or someone else',
- reason: 'Impersonation',
- },
- {
- label: 'This user is evading a ban',
- reason: 'BanEvasion',
- },
- {
- label: 'This user is too young to be using Revolt',
- reason: 'Underage',
- },
- {label: 'Something else not listed here', reason: 'NoneSpecified'},
- ] as Reason[];
-
- function ReasonsSelector({reasons}: {reasons: Reason[]}) {
- return (
- <>
-
- Why are you reporting this {type.toLowerCase()}? You can add more
- context after selecting a reason.
-
- {reasons.map(r => {
- return (
-