Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/actions/actionsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const ENCRYPTION = createRequestTypes('ENCRYPTION', [

export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']);
export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']);
export const CUSTOM_USER_STATUS = createRequestTypes('CUSTOM_USER_STATUS', ['SET']);
export const USERS_ROLES = createRequestTypes('USERS_ROLES', ['SET']);
export const VIDEO_CONF = createRequestTypes('VIDEO_CONF', [
'HANDLE_INCOMING_WEBSOCKET_MESSAGES',
Expand Down
13 changes: 13 additions & 0 deletions app/actions/customUserStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type Action } from 'redux';

import { CUSTOM_USER_STATUS } from './actionsTypes';
import { type ICustomUserStatus } from '../definitions';

export type TActionCustomUserStatus = Action & { customUserStatus: ICustomUserStatus[] };

export function setCustomUserStatus(customUserStatus: ICustomUserStatus[]): Action & { customUserStatus: ICustomUserStatus[] } {
return {
type: CUSTOM_USER_STATUS.SET,
customUserStatus
};
}
10 changes: 10 additions & 0 deletions app/definitions/ICustomUserStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { type TUserStatus } from './TUserStatus';

export interface ICustomUserStatus {
_id: string;
name: string;
statusType: TUserStatus;
_updatedAt: {
$date: number;
};
}
1 change: 1 addition & 0 deletions app/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export * from './TChangeAvatarViewContext';
export * from './IDataSelect';
export * from './TUserStatus';
export * from './IDeleteMessageBulkParams';
export * from './ICustomUserStatus';

export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
navigation: NativeStackNavigationProp<T & TNavigation, S>;
Expand Down
6 changes: 5 additions & 1 deletion app/definitions/redux/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import { type IRooms } from '../../reducers/rooms';
import { type IPreferences } from '../IPreferences';
import { type ICustomEmojis } from '../IEmoji';
import { type IUsersTyping } from '../../reducers/usersTyping';
import { type TActionCustomUserStatus } from '../../actions/customUserStatus';
import { type ICustomUserStatus } from '../ICustomUserStatus';

export interface IApplicationState {
settings: TSettingsState;
Expand Down Expand Up @@ -76,6 +78,7 @@ export interface IApplicationState {
troubleshootingNotification: ITroubleshootingNotification;
supportedVersions: ISupportedVersionsState;
inAppFeedback: IInAppFeedbackState;
customUserStatus: ICustomUserStatus[];
}

export type TApplicationActions = TActionActiveUsers &
Expand All @@ -99,4 +102,5 @@ export type TApplicationActions = TActionActiveUsers &
TActionUsersRoles &
TActionTroubleshootingNotification &
TActionSupportedVersions &
TInAppFeedbackAction;
TInAppFeedbackAction &
TActionCustomUserStatus;
5 changes: 4 additions & 1 deletion app/lib/services/restApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
type IRoomNotifications,
type IServerRoom,
type RoomType,
type SubscriptionType
type SubscriptionType,
type ICustomUserStatus
} from '../../definitions';
import { type TParams } from '../../definitions/ILivechatEditView';
import { type ILivechatTag } from '../../definitions/ILivechatTag';
Expand Down Expand Up @@ -1122,3 +1123,5 @@ export const getUsersRoles = async (): Promise<boolean | IRoleUser[]> => {

export const getSupportedVersionsCloud = (uniqueId?: string, domain?: string) =>
fetch(`https://releases.rocket.chat/v2/server/supportedVersions?uniqueId=${uniqueId}&domain=${domain}&source=mobile`);

export const getCustomUserStatus = (): Promise<ICustomUserStatus[]> => sdk.methodCallWrapper('listCustomUserStatus');
13 changes: 13 additions & 0 deletions app/reducers/customUserStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CUSTOM_USER_STATUS } from '../actions/actionsTypes';
import { type ICustomUserStatus, type TApplicationActions } from '../definitions';

export const initialState: ICustomUserStatus[] = [];

export default (state = initialState, action: TApplicationActions): ICustomUserStatus[] => {
switch (action.type) {
case CUSTOM_USER_STATUS.SET:
return action.customUserStatus;
default:
return state;
}
};
4 changes: 3 additions & 1 deletion app/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import usersRoles from './usersRoles';
import troubleshootingNotification from './troubleshootingNotification';
import supportedVersions from './supportedVersions';
import inAppFeedback from './inAppFeedback';
import customUserStatus from './customUserStatus';

export default combineReducers({
settings,
Expand Down Expand Up @@ -53,5 +54,6 @@ export default combineReducers({
usersRoles,
troubleshootingNotification,
supportedVersions,
inAppFeedback
inAppFeedback,
customUserStatus
});
17 changes: 15 additions & 2 deletions app/sagas/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ import { getSlashCommands } from '../lib/methods/getSlashCommands';
import { getUserPresence, subscribeUsersPresence } from '../lib/methods/getUsersPresence';
import { logout, removeServerData, removeServerDatabase } from '../lib/methods/logout';
import { subscribeSettings } from '../lib/methods/getSettings';
import { connect, loginWithPassword, login } from '../lib/services/connect';
import { saveUserProfile, registerPushToken, getUsersRoles } from '../lib/services/restApi';
import { loginWithPassword, login } from '../lib/services/connect';
import { saveUserProfile, registerPushToken, getUsersRoles, getCustomUserStatus } from '../lib/services/restApi';
import { setUsersRoles } from '../actions/usersRoles';
import { getServerById } from '../lib/database/services/Server';
import { appGroupSuiteName } from '../lib/methods/appGroup';
import appNavigation from '../lib/navigation/appNavigation';
import { showActionSheetRef } from '../containers/ActionSheet';
import { SupportedVersionsWarning } from '../containers/SupportedVersions';
import { isIOS } from '../lib/methods/helpers';
import { setCustomUserStatus } from '../actions/customUserStatus';

const getServer = state => state.server.server;
const loginWithPasswordCall = args => loginWithPassword(args);
Expand Down Expand Up @@ -226,6 +227,17 @@ const fetchUsersRoles = function* fetchRoomsFork() {
}
};

const fetchCustomUserStatus = function* fetchCustomUserStatusFork() {
try {
const customUserStatus = yield getCustomUserStatus();
if (customUserStatus.length) {
yield put(setCustomUserStatus(customUserStatus));
}
} catch (e) {
log(e);
}
};

const handleLoginSuccess = function* handleLoginSuccess({ user }) {
try {
getUserPresence(user.id);
Expand All @@ -242,6 +254,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
yield fork(fetchEnterpriseModulesFork, { user });
yield fork(subscribeSettingsFork);
yield fork(fetchUsersRoles);
yield fork(fetchCustomUserStatus);

setLanguage(user?.language);

Expand Down
100 changes: 75 additions & 25 deletions app/views/StatusView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigation } from '@react-navigation/native';
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';
import { FlatList, StyleSheet, View } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { useForm } from 'react-hook-form';
Expand Down Expand Up @@ -27,26 +27,32 @@ import Check from '../../containers/Check';
import { USER_STATUS_TEXT_MAX_LENGTH } from '../../lib/constants/maxLength';

interface IStatus {
id: TUserStatus;
_id: string;
name: string;
statusType: TUserStatus;
isCustom?: boolean;
}

const STATUS: IStatus[] = [
{
id: 'online',
name: 'Online'
_id: 'online',
name: 'Online',
statusType: 'online'
},
{
id: 'busy',
name: 'Busy'
_id: 'busy',
name: 'Busy',
statusType: 'busy'
},
{
id: 'away',
name: 'Away'
_id: 'away',
name: 'Away',
statusType: 'away'
},
{
id: 'offline',
name: 'Offline'
_id: 'offline',
name: 'Offline',
statusType: 'offline'
}
];

Expand All @@ -70,28 +76,53 @@ const styles = StyleSheet.create({
const Status = ({
statusType,
status,
setStatus
statusText,
setStatus,
isCustom,
isCustomSelected
}: {
statusType: IStatus;
status: TUserStatus;
setStatus: (status: TUserStatus) => void;
statusText: string;
setStatus: (status: TUserStatus, statusText: string) => void;
isCustom?: boolean;
isCustomSelected: boolean;
}) => {
const { id, name } = statusType;
const { _id, name } = statusType;
const acessibilityLabel = useMemo(() => {
if (status === _id) {
if (isCustom) {
return name;
}
return I18n.t('Current_Status');
}
return '';
}, [status, _id, isCustom]);

const checked = useMemo(() => {
if (isCustomSelected) {
return statusText === name;
}

return status === statusType._id;
}, [statusText, name, status, statusType]);

return (
<>
<List.Item
additionalAcessibilityLabel={`${status === id ? I18n.t('Current_Status') : ''}`}
additionalAcessibilityLabel={acessibilityLabel}
title={name}
translateTitle={!isCustom}
onPress={() => {
const key = `STATUS_${statusType.id.toUpperCase()}` as keyof typeof events;
const key = `STATUS_${statusType._id.toUpperCase()}` as keyof typeof events;
logEvent(events[key]);
if (status !== statusType.id) {
setStatus(statusType.id);
if (status !== statusType._id) {
setStatus(statusType.statusType, statusType.isCustom ? statusType.name : statusText);
}
}}
testID={`status-view-${id}`}
left={() => <StatusIcon size={24} status={statusType.id} />}
right={() => (status === id ? <Check /> : null)}
testID={`status-view-${_id}`}
left={() => <StatusIcon size={24} status={statusType.statusType} />}
right={() => (checked ? <Check /> : null)}
/>
<List.Separator />
</>
Expand All @@ -110,6 +141,7 @@ const StatusView = (): React.ReactElement => {
const Accounts_AllowInvisibleStatusOption = useSelector(
(state: IApplicationState) => state.settings.Accounts_AllowInvisibleStatusOption
);
const customUserStatus = useSelector((state: IApplicationState) => state.customUserStatus);

const {
control,
Expand Down Expand Up @@ -150,8 +182,11 @@ const StatusView = (): React.ReactElement => {
setHeader();
}, [isMasterDetail]);

const setStatus = (updatedStatus: TUserStatus) => {
setValue('status', updatedStatus);
const setStatus = (status: TUserStatus, statusText: string) => {
setValue('status', status);
if (statusText) {
setValue('statusText', statusText);
}
};

const setCustomStatus = async (status: TUserStatus, statusText: string) => {
Expand All @@ -173,7 +208,13 @@ const StatusView = (): React.ReactElement => {
sendLoadingEvent({ visible: false });
};

const statusType = Accounts_AllowInvisibleStatusOption ? STATUS : STATUS.filter(s => s.id !== 'offline');
const AllStatus = [...STATUS, ...customUserStatus.map(s => ({ ...s, isCustom: true }))];
const statusType = Accounts_AllowInvisibleStatusOption ? AllStatus : AllStatus.filter(s => s._id !== 'offline');

const isCustomSelected = useMemo(
() => !!customUserStatus.find(s => s.statusType === inputValues.status && s.name === inputValues.statusText),
[inputValues.status, inputValues.statusText, statusType]
);

const isStatusChanged = () => {
const { status } = inputValues;
Expand All @@ -195,8 +236,17 @@ const StatusView = (): React.ReactElement => {
<SafeAreaView testID='status-view'>
<FlatList
data={statusType}
keyExtractor={item => item.id}
renderItem={({ item }) => <Status statusType={item} status={inputValues.status} setStatus={setStatus} />}
keyExtractor={item => item._id}
renderItem={({ item }) => (
<Status
statusType={item}
statusText={inputValues.statusText}
status={inputValues.status}
setStatus={setStatus}
isCustom={item.isCustom}
isCustomSelected={isCustomSelected}
/>
)}
ListHeaderComponent={
<>
<ControlledFormTextInput
Expand Down
Loading