* copy directly from v1. will get working and then convert class to functional components
* screen showing up correctly. Need to convert lifestyle methods
* create channel button working
* save before refactor in prep for bringing in edit_channel functionality
* change function naming
* clean up lint
* clean up for PR review
* clean up for PR review
* remove hoardcoded styles
* add edit_channel screen
* add handlePatchChannel
* add custom useFormInput hook. use edit screen for both create and edit screens. edit or create screen mode determined by channel prop passed in as a channel or null
* rename edit_channel to create_or_edit_channel
* displayname, header, and purpose are now an object with value and onChange props, created from the useFormInput hook. Now only need to pass this new FormInput Prop to the edit_channel_info component and deconstruct there to get the onChange and value
* fix some lint errors
* fix some lint errors
* remove empty line
* pass intl into utils validate functions because they are not Hooks. add validation for displayName including translations.
* Move useFormInput hook to its own hooks file and import
* simplify
* remove editing prop. Was used to determine if the right button was enabled. It was always true for edit_channel screen and always false for create channel screen. The enableRightButton prop call back is was also used for the same reason.
* remove channeUrl editing references. This was not implemented on v1
* pass editing prop back into component and add back logic. When editing one field must change. when creating, just need to check that name is provided
* lint fixes
* fix typing issue for channel types
* scrolling ref should be fixed. Linting should pass now
* Linting should pass now
* require id field in partial Channel. fixes tsc
* remove everything related to renaming the channel URL. This has never been requred for mobile
* manage state with useReducer so that all actions/state in one location. This also removes the number of onXXX functions and reduces the number of functions in the component
* reorganize code. useEffects are at top. Move type and interfaces outside of function component
* Fix lint
* nit: invert if statement checking a negative
* use cneterChannelColor. in figma this is center channel text, but I verified theme color by comparing to SSO login text color
* Simple snapshot tests as a start
* Add more tests
* update snapshot
* add snapshot tests. Add tests for button enabling and disabling
* simplify test with destructuring.
* PR feedback. formatting changes. get user and teamid from one call
* remove FormInput hook and use value/setvalue convention for controlled components
* no need to setChannelDisplayName after creating/updating channel
* Just pass the setXXX function. Don't need to create as separate callback
* modify floatingTextInput component to allow placeholder text
* remove InteractionManager. PR nits
* mv EditChannelComponent into create_or_edit screen. Rename component from EditChannelInfo to ChannelInfoForm
* correct import path
* add IntlShape Type to function input. Wrap screen with withServerDatabase, not withIntl
* remove state setting function calls from inside the reducer. move close function outside of the component. remove setRightButton and rightButton and place rightbutton in initial appState
* move editing const after useX oneliners and before useCallback, useEffect, and useReducers
* rightButton
- useMemo to memoize an object with dependencies
- move out of the appState
emitCanSaveChannel
- wrap with useCallback
onCreateChannel
- wrap with useCallback
onUpdateChannel
- wrap with useCallback
useEffect Navigation
- use the callbacks as dependencies in stead of the depencies of those
callbacks.
* wrap all formatted message with useMemo()
wrap all onXXXChangeText with useCallback and add deps
move all oneliner derived constants directly after useState useMemo
* remove useMemo from formatted text
* switchToCHannel is still not working. failing at
const channel: ChannelModel = await member.channel.fetch();
* use prepareMyChannelsForTeam to update db tables for new channel
* add placeholder text color
* Attach open edit channel screen to `Set Header` button in channel intro view
port SectionItem from V1 and us to add a Switch for setting private/public channel
hook up the plus icon in the channel list header to create a channel (temporary fix to allow debugging)
add new queryChannelsInfoById and queryCurrentChannelInfo query functions
update text for create screen text inputs
* Fix styles and fix actions
* Add autocomplete, fix patch, and address design feedback
* Address feedback
* Add margin between icon and label on Make Private
* Address feedback
* Address feedback
* Address feedback and fix channel list not updating when the channel gets created
* Address feedback and directly add the channel to the default category
* Render at-mentions as Members if no channelId is set
* Display autocomplete on iOS
Co-authored-by: Jason Frerich <jason.frerich@mattermost.com>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
414 lines
14 KiB
TypeScript
414 lines
14 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {General} from '@constants';
|
|
import {buildQueryString} from '@utils/helpers';
|
|
|
|
import {PER_PAGE_DEFAULT} from './constants';
|
|
|
|
export interface ClientUsersMix {
|
|
createUser: (user: UserProfile, token: string, inviteId: string) => Promise<UserProfile>;
|
|
patchMe: (userPatch: Partial<UserProfile>) => Promise<UserProfile>;
|
|
patchUser: (userPatch: Partial<UserProfile> & {id: string}) => Promise<UserProfile>;
|
|
updateUser: (user: UserProfile) => Promise<UserProfile>;
|
|
demoteUserToGuest: (userId: string) => Promise<any>;
|
|
getKnownUsers: () => Promise<string[]>;
|
|
sendPasswordResetEmail: (email: string) => Promise<any>;
|
|
setDefaultProfileImage: (userId: string) => Promise<any>;
|
|
login: (loginId: string, password: string, token?: string, deviceId?: string, ldapOnly?: boolean) => Promise<UserProfile>;
|
|
loginById: (id: string, password: string, token?: string, deviceId?: string) => Promise<UserProfile>;
|
|
logout: () => Promise<any>;
|
|
getProfiles: (page?: number, perPage?: number, options?: Record<string, any>) => Promise<UserProfile[]>;
|
|
getProfilesByIds: (userIds: string[], options?: Record<string, any>) => Promise<UserProfile[]>;
|
|
getProfilesByUsernames: (usernames: string[]) => Promise<UserProfile[]>;
|
|
getProfilesInTeam: (teamId: string, page?: number, perPage?: number, sort?: string, options?: Record<string, any>) => Promise<UserProfile[]>;
|
|
getProfilesNotInTeam: (teamId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
|
|
getProfilesWithoutTeam: (page?: number, perPage?: number, options?: Record<string, any>) => Promise<UserProfile[]>;
|
|
getProfilesInChannel: (channelId: string, page?: number, perPage?: number, sort?: string) => Promise<UserProfile[]>;
|
|
getProfilesInGroupChannels: (channelsIds: string[]) => Promise<{[x: string]: UserProfile[]}>;
|
|
getProfilesNotInChannel: (teamId: string, channelId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
|
|
getMe: () => Promise<UserProfile>;
|
|
getUser: (userId: string) => Promise<UserProfile>;
|
|
getUserByUsername: (username: string) => Promise<UserProfile>;
|
|
getUserByEmail: (email: string) => Promise<UserProfile>;
|
|
getProfilePictureUrl: (userId: string, lastPictureUpdate: number) => string;
|
|
getDefaultProfilePictureUrl: (userId: string) => string;
|
|
autocompleteUsers: (name: string, teamId: string, channelId?: string, options?: Record<string, any>) => Promise<{users: UserProfile[]; out_of_channel?: UserProfile[]}>;
|
|
getSessions: (userId: string) => Promise<Session[]>;
|
|
checkUserMfa: (loginId: string) => Promise<{mfa_required: boolean}>;
|
|
attachDevice: (deviceId: string) => Promise<any>;
|
|
searchUsers: (term: string, options: any) => Promise<UserProfile[]>;
|
|
getStatusesByIds: (userIds: string[]) => Promise<UserStatus[]>;
|
|
getStatus: (userId: string) => Promise<UserStatus>;
|
|
updateStatus: (status: UserStatus) => Promise<UserStatus>;
|
|
updateCustomStatus: (customStatus: UserCustomStatus) => Promise<{status: string}>;
|
|
unsetCustomStatus: () => Promise<{status: string}>;
|
|
removeRecentCustomStatus: (customStatus: UserCustomStatus) => Promise<{status: string}>;
|
|
}
|
|
|
|
const ClientUsers = (superclass: any) => class extends superclass {
|
|
createUser = async (user: UserProfile, token: string, inviteId: string) => {
|
|
this.analytics.trackAPI('api_users_create');
|
|
|
|
const queryParams: any = {};
|
|
|
|
if (token) {
|
|
queryParams.t = token;
|
|
}
|
|
|
|
if (inviteId) {
|
|
queryParams.iid = inviteId;
|
|
}
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString(queryParams)}`,
|
|
{method: 'post', body: user},
|
|
);
|
|
};
|
|
|
|
patchMe = async (userPatch: Partial<UserProfile>) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute('me')}/patch`,
|
|
{method: 'put', body: userPatch},
|
|
);
|
|
};
|
|
|
|
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
|
|
this.analytics.trackAPI('api_users_patch');
|
|
|
|
return this.doFetch(
|
|
`${this.getUserRoute(userPatch.id)}/patch`,
|
|
{method: 'put', body: userPatch},
|
|
);
|
|
};
|
|
|
|
updateUser = async (user: UserProfile) => {
|
|
this.analytics.trackAPI('api_users_update');
|
|
|
|
return this.doFetch(
|
|
`${this.getUserRoute(user.id)}`,
|
|
{method: 'put', body: user},
|
|
);
|
|
};
|
|
|
|
demoteUserToGuest = async (userId: string) => {
|
|
this.analytics.trackAPI('api_users_demote_user_to_guest');
|
|
|
|
return this.doFetch(
|
|
`${this.getUserRoute(userId)}/demote`,
|
|
{method: 'post'},
|
|
);
|
|
};
|
|
|
|
getKnownUsers = async () => {
|
|
this.analytics.trackAPI('api_get_known_users');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/known`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
sendPasswordResetEmail = async (email: string) => {
|
|
this.analytics.trackAPI('api_users_send_password_reset');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/password/reset/send`,
|
|
{method: 'post', body: {email}},
|
|
);
|
|
};
|
|
|
|
setDefaultProfileImage = async (userId: string) => {
|
|
this.analytics.trackAPI('api_users_set_default_profile_picture');
|
|
|
|
return this.doFetch(
|
|
`${this.getUserRoute(userId)}/image`,
|
|
{method: 'delete'},
|
|
);
|
|
};
|
|
|
|
login = async (loginId: string, password: string, token = '', deviceId = '', ldapOnly = false) => {
|
|
this.analytics.trackAPI('api_users_login');
|
|
|
|
if (ldapOnly) {
|
|
this.analytics.trackAPI('api_users_login_ldap');
|
|
}
|
|
|
|
const body: any = {
|
|
device_id: deviceId,
|
|
login_id: loginId,
|
|
password,
|
|
token,
|
|
};
|
|
|
|
if (ldapOnly) {
|
|
body.ldap_only = 'true';
|
|
}
|
|
|
|
const {data} = await this.doFetch(
|
|
`${this.getUsersRoute()}/login`,
|
|
{
|
|
method: 'post',
|
|
body,
|
|
headers: {'Cache-Control': 'no-store'},
|
|
},
|
|
false,
|
|
);
|
|
|
|
return data;
|
|
};
|
|
|
|
loginById = async (id: string, password: string, token = '', deviceId = '') => {
|
|
this.analytics.trackAPI('api_users_login');
|
|
const body: any = {
|
|
device_id: deviceId,
|
|
id,
|
|
password,
|
|
token,
|
|
};
|
|
|
|
const {data} = await this.doFetch(
|
|
`${this.getUsersRoute()}/login`,
|
|
{method: 'post', body},
|
|
);
|
|
|
|
return data;
|
|
};
|
|
|
|
logout = async () => {
|
|
this.analytics.trackAPI('api_users_logout');
|
|
|
|
const response = await this.doFetch(
|
|
`${this.getUsersRoute()}/logout`,
|
|
{method: 'post'},
|
|
);
|
|
|
|
return response;
|
|
};
|
|
|
|
getProfiles = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
|
this.analytics.trackAPI('api_profiles_get');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString({page, per_page: perPage, ...options})}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getProfilesByIds = async (userIds: string[], options = {}) => {
|
|
this.analytics.trackAPI('api_profiles_get_by_ids');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/ids${buildQueryString(options)}`,
|
|
{method: 'post', body: userIds},
|
|
);
|
|
};
|
|
|
|
getProfilesByUsernames = async (usernames: string[]) => {
|
|
this.analytics.trackAPI('api_profiles_get_by_usernames');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/usernames`,
|
|
{method: 'post', body: usernames},
|
|
);
|
|
};
|
|
|
|
getProfilesInTeam = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '', options = {}) => {
|
|
this.analytics.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString({...options, in_team: teamId, page, per_page: perPage, sort})}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getProfilesNotInTeam = async (teamId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
|
this.analytics.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
|
|
|
const queryStringObj: any = {not_in_team: teamId, page, per_page: perPage};
|
|
if (groupConstrained) {
|
|
queryStringObj.group_constrained = true;
|
|
}
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getProfilesWithoutTeam = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
|
this.analytics.trackAPI('api_profiles_get_without_team');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString({...options, without_team: 1, page, per_page: perPage})}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getProfilesInChannel = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '') => {
|
|
this.analytics.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
|
|
|
const queryStringObj = {in_channel: channelId, page, per_page: perPage, sort};
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getProfilesInGroupChannels = async (channelsIds: string[]) => {
|
|
this.analytics.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/group_channels`,
|
|
{method: 'post', body: channelsIds},
|
|
);
|
|
};
|
|
|
|
getProfilesNotInChannel = async (teamId: string, channelId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
|
this.analytics.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
|
|
|
const queryStringObj: any = {in_team: teamId, not_in_channel: channelId, page, per_page: perPage};
|
|
if (groupConstrained) {
|
|
queryStringObj.group_constrained = true;
|
|
}
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getMe = async () => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute('me')}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getUser = async (userId: string) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute(userId)}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getUserByUsername = async (username: string) => {
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/username/${username}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getUserByEmail = async (email: string) => {
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/email/${email}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getProfilePictureUrl = (userId: string, lastPictureUpdate: number) => {
|
|
const params: any = {};
|
|
|
|
if (lastPictureUpdate) {
|
|
params._ = lastPictureUpdate;
|
|
}
|
|
|
|
return `${this.getUserRoute(userId)}/image${buildQueryString(params)}`;
|
|
};
|
|
|
|
getDefaultProfilePictureUrl = (userId: string) => {
|
|
return `${this.getUserRoute(userId)}/image/default`;
|
|
};
|
|
|
|
autocompleteUsers = async (name: string, teamId: string, channelId?: string, options = {
|
|
limit: General.AUTOCOMPLETE_LIMIT_DEFAULT,
|
|
}) => {
|
|
const query: Dictionary<any> = {
|
|
in_team: teamId,
|
|
name,
|
|
};
|
|
if (channelId) {
|
|
query.in_channel = channelId;
|
|
}
|
|
if (options.limit) {
|
|
query.limit = options.limit;
|
|
}
|
|
return this.doFetch(`${this.getUsersRoute()}/autocomplete${buildQueryString(query)}`, {
|
|
method: 'get',
|
|
});
|
|
};
|
|
|
|
getSessions = async (userId: string) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute(userId)}/sessions`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
checkUserMfa = async (loginId: string) => {
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/mfa`,
|
|
{method: 'post', body: {login_id: loginId}},
|
|
);
|
|
};
|
|
|
|
attachDevice = async (deviceId: string) => {
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/sessions/device`,
|
|
{method: 'put', body: {device_id: deviceId}},
|
|
);
|
|
};
|
|
|
|
searchUsers = async (term: string, options: any) => {
|
|
this.analytics.trackAPI('api_search_users');
|
|
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/search`,
|
|
{method: 'post', body: {term, ...options}},
|
|
);
|
|
};
|
|
|
|
getStatusesByIds = async (userIds: string[]) => {
|
|
return this.doFetch(
|
|
`${this.getUsersRoute()}/status/ids`,
|
|
{method: 'post', body: userIds},
|
|
);
|
|
};
|
|
|
|
getStatus = async (userId: string) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute(userId)}/status`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
updateStatus = async (status: UserStatus) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute(status.user_id)}/status`,
|
|
{method: 'put', body: status},
|
|
);
|
|
};
|
|
|
|
updateCustomStatus = async (customStatus: UserCustomStatus) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute('me')}/status/custom`,
|
|
{method: 'put', body: customStatus},
|
|
);
|
|
};
|
|
|
|
unsetCustomStatus = async () => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute('me')}/status/custom`,
|
|
{method: 'delete'},
|
|
);
|
|
};
|
|
|
|
removeRecentCustomStatus = async (customStatus: UserCustomStatus) => {
|
|
return this.doFetch(
|
|
`${this.getUserRoute('me')}/status/custom/recent/delete`,
|
|
{method: 'post', body: customStatus},
|
|
);
|
|
};
|
|
};
|
|
|
|
export default ClientUsers;
|