Merge 4.25.0 into single-server (#3790)
* Chore: Migrate DefaultBrowserView to Typescript (#3488) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * Chore: Migrate PickerView to Typescript (#3501) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate AttachmentView to Typescript (#3483) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate MarkdownTableView to Typescript (#3500) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate E2EEncryptionSecurityView to Typescript (#3489) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate E2EEnterYourPasswordView to Typescript (#3490) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate E2EHowItWorksView to Typescript (#3492) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate E2ESaveYourPasswordView to Typescript (#3493) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate ForgotPasswordView to Typescript (#3496) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate ForwardLivechatView to Typescript (#3497) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate JitsiMeetView to Typescript (#3498) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Push notifications user preference not syncing correctly (#3494) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Display prefs showing wrong header icon on tablet (#3510) * Merge 4.22.0 into master (#3523) * Tests: Make Detox work on Android (#3051) * Chore: Migrate NewMessageView to Typescript (#3502) * Chore: Migrate ScreenLockConfigView to Typescript (#3517) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate ScreenLockedView to Typescript (#3515) * Chore: Migrate SecurityPrivacyView to Typescript (#3518) * Chore: Migrate SelectListView to Typescript (#3519) * Chore: Migrate SelectServerView to Typescript (#3521) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * Chore: Migrate SetUsernameView to Typescript (#3526) * Chore: Migrate ThemeView to Typescript (#3522) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * Chore: Migrate StatusView to Typescript (#3527) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * Chore: Migrate ShareListView to Typescript (#3459) Co-authored-by: Gerzon Z <gerzonc@icloud.com> * Chore: Migrate TeamChannelsView to Typescript (#3532) Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com> * Language update from LingoHub 🤖 (#3529) Project Name: Rocket.Chat.ReactNative Project Link: https://translate.lingohub.com/rocketchat/dashboard/rocket-dot-chat-dot-reactnative User: Robot LingoHub Easy language translations with LingoHub 🚀 Co-authored-by: Robot LingoHub <robot@lingohub.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate react-navigation to TypeScript (#3480) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * Bump version to 4.23.0 (#3546) * [FIX] Certificate stops working after app update on iOS (#3537) * [IMPROVE] Connection stability (#3531) * [NEW] Permission for uploading files (#3505) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Files screen stopped listing content on server 4.2 (#3541) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate ModalBlockView to Typescript (#3503) * Chore: Migrate ModalBlockView to Typescript * minor tweaks * update the navigator Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate SelectedUsersView to Typescript (#3520) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVE] Remove Omnichannel visitor's navigation history (#3534) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Merge 4.23.0 into master (#3574) * [FIX] Download video/quicktime in iOS (#3581) * Chore: Migrate Redux to Typescript PoC (#3565) * Chore: Migrate Model's folder to Typescript (#3564) * Chore: Migrate lib user preferences to Typescript (#3578) * Chore: Update React Native Device Info to 8.4.8 (#3560) * [FIX] Roles rendering on dark theme (#3589) * fix: Add height verification to fix modal dimension (#3573) * chore: Change the lib `@types/url-parse` to devDependencies (#3585) * [FIX] teams.removeMembers mobile usage (#3557) * Chore: Migrate DisplayPrefsView to Typescript (#3555) * Chore: Migrate Utils Folder to Typescript (#3544) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * Chore: Migrate ThreadMessagesView to Typescript (#3538) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> * [FIX] RoomInfoView displaying different info depending on the origin (#3586) Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com> * [FIX] Message parser switch not updating field properly (#3576) * [FIX] Lint not ignoring Markdown props (#3600) * Bump version to 4.24.0 (#3601) * Chore: Migrate notification/push to Typescript (#3587) * chore: migrate connect to ts and add tests * chore: add more tests * Chore: Update react-native-device-info patch-package and pods (#3605) * [FIX] App crashes when entering server after applying certificate (Android) (#3579) * chore: migrate redux module encryption to typescript * chore: migrate customEmoji to typescript and add tests * chore: create IPreferences interface * chore: migrate redux module sortPreferences to typescript * chore: fix IPreference interface and organize import * chore: migrate to typescript * chore: migrate usersTyping to typescript * Add DiscussionDetails and Item for DiscussionsView; update ThreadDetails, BackgroundContainer and DiscussionsView * chore: migrate settings to typescript * chore: add interface to IStateAplication * chore: migrate redux module room to typescript * update definitions * chore: fix error on error interface * [FIX] Joining and leaving messages in teams (#3591) Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * minor tweak * [FIX] TypeScript's errors raised by HOCs (#3535) Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * chore: migrate redux module roles to typescript * wip: add IRoles to IAplicationState interface * add storybooks, update snapshots and DiscussionsView * Minor tweaks * Fix lint * Remove unused import * chore: migrate redux module inviteUsers to typescript * chore: migrate messages action to typescript * chore: fix any interface and change null to empty string * chore: implements IAplicationState on type * chore: remove mapDispatchToProps and continue ts migration * chore: fix types and apply IAplicationState to types * Migrate redux server action/reducer to ts * add tests * [FIX] App crashes when opening a notification while app is closed (#3629) * [FIX] makeThreadName asserting undefined as non-null (#3628) * [FIX] Threads' pagination not working (#3631) * update tests * chore: update settings value types * Send missing params to selectServerRequest * [IMPROVE] Convert HEIC images to JPG and remove compression (#3633) * update interface * update action definition * Move onDiscussionpress logic on message, update SearchHeader and DiscussionDetails component, add useLayoutEffect at DiscussionsView * Update interfaces and minor tweaks to DiscussionsView screen and components * Fix navigation logic and update interfaces * Minor tweaks * Undo change on project.pbxproj * Update project.pbxproj * Update project.pbxproj * Remove style.ts * Minor tweak * update snapshots * Merge 4.24.0 into master (#3648) * Chore: Change console.log to console.error when logging error * chore: add as string to fix type * Fix lint * fix types * test * Remove console.log * test * [FIX] StoryShots not working for async rendered components (#3677) * remove console.log * Add missing DiscussionsView snapshot * fix build and useless done and async generator * update snapshot * Chore: fix build and useless done and async generator (#3678) * fix build and useless done and async generator * update snapshot * Chore: Migrate Database to Typescript (#3580) Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate redux module permissions to typescript (#3630) * Chore: Migrate redux module share to typescript (#3612) * chore: migrate redux module share to typescript * chore: fix types * chore: update types * chore: migrate redux module share to typescript * remove double import * chore: fix import * Chore: Migrate redux module createChannel to typescript (#3602) * chore: migrate createChannel to ts and add tests * chore: fix naming * chore: add more types and remove mapDispatchToProps from components * remove todo * update tests * chore: migrate interface to reducer and fix errors on return * chore: insert IApplicationState to mapStateToProps state type * Remove spread * fix type * fix import and state type Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate redux module app to typescript (#3598) * chore: migrate activeUsers reducer and action to TS * chore: init types folder and set redux and BaseScreen interface * chore: remove mapDispatchToProps to use dispatch prop and clear some types * chore: type selectedUsers action and reducer and improvement in the code of other files * chore: move IUser to base types * chore: move state props to ISelectedUsersViewProps * chore: create mocketStore * chore: remove applyAppStateMiddleware * test: create activeUser and selectedUser tests * test: add more selectedUsers tests * chore: fix action type * chore: move types to definition folder and fix imports * chore: remove unused const * chore: migrate redux tests to reducer folder and add eslint jest plugin * chore: exprot initial state and then import on tests * chore: move interfaces to reducer and import on screen * chore: set eslint-plugin-jest version to 24.7.0 * chore: fix IUser import * chore: update interfaces and types names * chore: update definitions * chore: update IBaseScreen definitions * chore: init reducer/app migration to ts * chore: add tests and migrate RootEnum * wip: migrate fixed consts to RootEnum * chore: remove redux action inferences * fix types Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate redux module createDiscussion to typescript (#3604) * chore: migrate createDiscussion to ts and add tests * chore: add TActionCreateDiscussion to TApplicationActions * fix types * update types * fix types Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] ios-testflight-experimental unable to find cache (#3684) * Chore: Remove Non-null assertion operator in ThreadMessagesView (#3632) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate CannedResponsesListView to Typescript (#3553) * Chore: Migrate CannedResponsesListView to TS * Moved IcannedResponse to definitions and fixed the index * Chore: Migrate CannedResponseDetail to TS * minor tweaks * refactor: update new types and interfaces for use ISubscription * fix lint error and canned responses's dropdown Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate LivechatEditView to Typescript (#3499) * Chore: Migrate LivechatEditView to Typescript * refactor: minor tweak * refactor: fix the interfaces for input * refactor: fix lint erros * minor tweak with new navigation types * function * iroom tweak * livechateditview tweak * TextInput tweak * refactor: update new types and interfaces for use ISubscription * refactor to default useState type * change the component name in SearchBox * changed state type Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Gerzon Z <gerzonc@icloud.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Regression: Message press navigating to empty RoomView (#3680) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Regression: Prevent duplicated .jpg on file upload (#3658) * [FIX] Regression: Prevent duplicated .jpg on file upload * refactor to all files typed as image/jpeg * isolate regexp to function * refactor forceJpgExtension * clean * minor tweak * [FIX] Regression: Prevent duplicated .jpg on file upload * refactor to all files typed as image/jpeg * isolate regexp to function * refactor forceJpgExtension * clean * minor tweak * refactored comment * Chore: Migrate lib/utils to TypeScript (#3637) * Migrate utils to TypeScript * Add @types/semver * Refactor compareServerVersion(currentVersion, oldVersion, func) to compareServerVersion(current, func, oldVersion) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate readMessages to TS (#3669) * Migrate readMessages to TS * Update IRocketChat interface * [FIX] Unnecessary login dispatch on adding new server (#3693) * [FIX] Disable tap gesture on call messages (#3694) * [IMPROVE] Keep biometry option from last session (#3668) Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com> Co-authored-by: Reinaldo Neto <reinaldonetof@hotmail.com> * Fix reactotron multiple connections (#3622) * Chore: Fix rocketchat interface (#3705) * Chore: Migrate logout to Typescript (#3688) * [NEW] Stream to get individual presence updates (#3606) Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com> * [FIX] Inject Redux store to prevent/remove require cycles (#3691) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate lib/rocketchat.js to TS - structure PoC (#3661) Co-authored-by: Diego Mello <diegolmello@gmail.com> Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com> * [FIX] #3606 merged using wrong JS SDK branch (#3709) * [FIX] Remove deprecated database methods and other database operations (#3686) * Fix PK error on subscriptions/room * Instead of checking for pending update, wrap the call on a try catch and return null in case of error * Generate delete operations before create/update to prevent errors * Apply same logic on encryption * Fix database operations on getRoles * Fix a few database issues found on Bugsnag on ThreadMessagesView * Run prettier :( * Chore: Add REST API definitions from server (#3721) * create first definitions * chore: implements get and post types * fix lint * add ts-ignore * add teams.removeRoom method * Remove unused endpoints Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Move some methods to SDK (#3736) * [IMPROVE] Add support for ephemeral messages inside threads (#3687) * Chore: dehydrate small server requests away from rocketchat.js (#3740) * Bump version to 4.25.0 (#3745) * [Snyk] Security upgrade url-parse from 1.5.1 to 1.5.6 (#3746) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-URLPARSE-2401205 * Language update from LingoHub 🤖 on 2022-02-14Z (#3730) * Language update from LingoHub 🤖 Project Name: Rocket.Chat.ReactNative Project Link: https://translate.lingohub.com/rocketchat/dashboard/rocket-dot-chat-dot-reactnative User: Robot LingoHub Easy language translations with LingoHub 🚀 * remove draft gl Co-authored-by: Robot LingoHub <robot@lingohub.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate methods/getSingleMessage to TS (#3700) * migrate getSingleMessage to TS * minor tweak * Chore: Migrate methods/getRooms to TS (#3702) * migrate getRooms to TS * add sdk and set any types * Moved the new variable around and added ts-ignore to follow the pattern from /services/restApi.ts Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate updateMessages to Typescript (#3715) * Chore: Migrate selector/login to TS (#3731) * migrate selector/login to TS * Fix lint errors * set aliases for returns * Chore: Migrate helpers/parseUrls to Typescript (#3735) * Chore: Migrate methods/helpers/parseQuery to Typescript (#3742) * Chore: Migrate methods/helpers/parseQuery to Typescript * tweak in example * Chore: Migrate app/commands to typescript (#3697) * Chore: Migrate lib/encryption folder to TypeScript (#3639) * Initial commit * add types/bytebuffer, add type definitions to params and update interfaces * add more types and type assertions * update types * change bang operator by type assertion and update class variables definitions * add types for deferred class * minor tweaks on types definitions * add ts-ignore * Update encryption.ts * update deferred and encryption * update encryption.ts * Update room.ts * update toDecrypt type * initialize sessionKeyExportedString * remove return types * Chore: Migrate redux actions/enterpriseModules to TS (#3698) * migrate enterpriseModules to TS * update test file * Chore: Migrate database/services and database/utils to TS (#3708) * migrate database services and utils to ts * Migrate tests Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate buildMessage to TS (#3732) * migrate buildMessage to TS * Fix lint * minor tweak * minor tweaks * Chore: Migrate getPermissions to Typescript (#3720) * Migrating... * Fix IPermission * Playing with types * Remove `as const` * Fix lint * Fix test * Apply sdk * Fix lint and autocomplete * [FIX] Add search and fix pagination for omnichannels departments (#3621) * [FIX] Search and pagination for omnichannels departments * pagination complete * minor tweak * renamed a param and workaround for a ux bug * fix style of flatlist and search as header scrollable * stick the header * Merge branch 'fix.forward-department-list' of https://github.com/RocketChat/Rocket.Chat.ReactNative into fix.forward-department-list * refactor pagination * fix value type * refactor render search * refactor layout * make ts happy * Chore: Migrate Markdown to Typescript (#3558) * Chore: Migrate Markdown to TS * Chore: Migrate Markdown to TS * minor tweak * added preview where markdown was preview and fixed params within markdown * removed ts-ignore * fix lint * removed numbersofline={0} and default value to numberOfLines=1 * change how to import markdown preview and remove numberOfLines * using useTheme inside markdownPreview and remove theme from components * minor tweak on interfaces * isNewMarkdown return as boolean * minor tweaks * minor tweaks * removed unused component * fixed markdown stories * updated snapshot because removed numberOfLines={0} from message/content * create IEmoji.ts in definitions and refactor all places where getCustomEmoji was called * onLinkPress typed * todo: refactor navtoroominfo * formatText.test.ts * markdown stories to typescript too * minor tweak * IMessage definition * refactor: update new types and interfaces for use ISubscription * refactor: update threadItem for use new MarkdownPreview * refactor: rollback wrong file commited * formatHyperlink * fix lint * updated item story shot * refactor and refactor some types * Remove non-null assertion * Minor change on useRealName * tweak Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate methods/callJitsi to typescript (#3660) * chore: migrate callJitsi to typescript * change fixed string to type * wip * wip * back to old times :) * back to typescript * Chore: Migrate redux module room to typescript (#3636) * chore: migrate redux module room to typescript and remove dispatch on dependencies * chore: add tests to redux module room * chore: create ERoomType and use on implemention * chore: update enum name * fix test id * Chore: Migrate redux module login to typescript (#3647) * chore: migrate redux module login to typescript chore: update redux module login tests * update workers * wip * fix type * remove partial * add more status * migrate the rest of the stuff to typescript * fix tests and types * fix types and tests * Chore: Migrate method getSettings to typescript (#3703) * chore: migrate getSettings to typescript and and some types * chore: remove this and add current to code * chore: add current * Chore: Migrate getCustomEmojis to TS (#3724) * update customEmoji interface and getCustomEmoji * add sdk * updated emojiCustom rest definition * minor refactor * update params object * [FIX] getRooms request using param with wrong name (#3761) * Chore: Migrate methods/getRoomInfo to TS (#3695) * migrate getRoomInfo to TS * update room type * update types * Fix lint error * Chore: Migrate getSlashCommands to TS (#3711) * migrate getSlashCommands to TS * use sdk and update getSlashCommands * minor tweak * Remove implicit anys Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate getUsersPresence to TS (#3717) * migrate getUsersPresence to ts * use sdk and remove this context from getUsersPresence * Chore: Migrate loadMissedMessages to typescript (#3704) * chore: migrate loadMissedMessages to typescript * remove loaderItem * remove this from functions * Chore: Migrate methods/getRoles to Typescript (#3741) * chore: migrate getRoles to ts * chore: removing unused const * chore: minor tweak * Type batch Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate methods/loadMessagesForRoom to Typescript (#3701) * chore: change loadMessagesForRoom to typescript * minor tweak * chore: minor tweaks after merge with developer * chore: minor tweaks after merge with developer * chore: minor tweak * chore: minor tweaks * Fix return Co-authored-by: Diego Mello <diegolmello@gmail.com> Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com> * Chore: Migrate methods/sendFileMessage to typescript (#3683) * chore: start the migration * chore: update sendFileMessage to ts * chore: removing an `any` from uploadQueue * chore: minor tweak * chore: minor tweak * chore: minor tweaks after merge with developer * chore: minor tweak after merge develop into current * [FIX] Differ to Last Session Authenticated (#3667) * [FIX] Differ to Last Session Authenticated * Added timesync * [FIX] Differ to Last Session Authenticated * Added timesync * timesync tweaks * refactor diffLastLocalSession and saveLastLocalAuthentication * did a race * Update comment in app/utils/localAuthentication.ts Co-authored-by: Diego Mello <diegolmello@gmail.com> * refactor getServerTimeSync and when use this route * tweak Co-authored-by: Diego Mello <diegolmello@gmail.com> * Chore: Migrate methods/loadNextMessages to typescript (#3719) * feat: update loadNextMessages to ts * minor tweak * chore: minor tweaks after merge with developer * chore: migrate getFileUrlFromMessage to ts (#3734) * [IMPROVE] Team system messages feedback (#3771) (#3772) * almost there * Update stories Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Co-authored-by: Gerzon Z <gerzonc@icloud.com> Co-authored-by: Anant Bhasin <38764067+aKn1ghtOut@users.noreply.github.com> Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com> Co-authored-by: lingohub[bot] <69908207+lingohub[bot]@users.noreply.github.com> Co-authored-by: Robot LingoHub <robot@lingohub.com> Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com> Co-authored-by: Danish Ahmed Mirza <77742477+try-catch-stack@users.noreply.github.com> Co-authored-by: Reinaldo Neto <reinaldonetof@hotmail.com> Co-authored-by: Snyk bot <snyk-bot@snyk.io>
This commit is contained in:
parent
14f0108325
commit
fc9e9a4f2a
|
@ -359,7 +359,7 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Test
|
name: Test
|
||||||
command: |
|
command: |
|
||||||
yarn test
|
yarn test -w 8
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Codecov
|
name: Codecov
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import initStoryshots from '@storybook/addon-storyshots';
|
import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
|
||||||
|
import { render } from '@testing-library/react-native';
|
||||||
|
|
||||||
jest.mock('rn-fetch-blob', () => ({
|
jest.mock('rn-fetch-blob', () => ({
|
||||||
fs: {
|
fs: {
|
||||||
|
@ -19,4 +20,15 @@ jest.mock('react-native-file-viewer', () => ({
|
||||||
jest.mock('../app/lib/database', () => jest.fn(() => null));
|
jest.mock('../app/lib/database', () => jest.fn(() => null));
|
||||||
global.Date.now = jest.fn(() => new Date('2019-10-10').getTime());
|
global.Date.now = jest.fn(() => new Date('2019-10-10').getTime());
|
||||||
|
|
||||||
initStoryshots();
|
const converter = new Stories2SnapsConverter();
|
||||||
|
|
||||||
|
initStoryshots({
|
||||||
|
test: ({ story, context }) => {
|
||||||
|
const snapshotFilename = converter.getSnapshotFileName(context);
|
||||||
|
const storyElement = story.render();
|
||||||
|
const { update, toJSON } = render(storyElement);
|
||||||
|
update(storyElement);
|
||||||
|
const json = toJSON();
|
||||||
|
expect(JSON.stringify(json)).toMatchSpecificSnapshot(snapshotFilename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -144,7 +144,7 @@ android {
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode VERSIONCODE as Integer
|
versionCode VERSIONCODE as Integer
|
||||||
versionName "4.24.0"
|
versionName "4.25.0"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
if (!isFoss) {
|
if (!isFoss) {
|
||||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { connect } from 'react-redux';
|
||||||
import { SetUsernameStackParamList, StackParamList } from './definitions/navigationTypes';
|
import { SetUsernameStackParamList, StackParamList } from './definitions/navigationTypes';
|
||||||
import Navigation from './lib/Navigation';
|
import Navigation from './lib/Navigation';
|
||||||
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
||||||
import { ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app';
|
import { RootEnum } from './definitions';
|
||||||
// Stacks
|
// Stacks
|
||||||
import AuthLoadingView from './views/AuthLoadingView';
|
import AuthLoadingView from './views/AuthLoadingView';
|
||||||
// SetUsername Stack
|
// SetUsername Stack
|
||||||
|
@ -56,13 +56,13 @@ const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail
|
||||||
}}>
|
}}>
|
||||||
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
|
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
|
||||||
<>
|
<>
|
||||||
{root === ROOT_LOADING ? <Stack.Screen name='AuthLoading' component={AuthLoadingView} /> : null}
|
{root === RootEnum.ROOT_LOADING ? <Stack.Screen name='AuthLoading' component={AuthLoadingView} /> : null}
|
||||||
{root === ROOT_OUTSIDE ? <Stack.Screen name='OutsideStack' component={OutsideStack} /> : null}
|
{root === RootEnum.ROOT_OUTSIDE ? <Stack.Screen name='OutsideStack' component={OutsideStack} /> : null}
|
||||||
{root === ROOT_INSIDE && isMasterDetail ? (
|
{root === RootEnum.ROOT_INSIDE && isMasterDetail ? (
|
||||||
<Stack.Screen name='MasterDetailStack' component={MasterDetailStack} />
|
<Stack.Screen name='MasterDetailStack' component={MasterDetailStack} />
|
||||||
) : null}
|
) : null}
|
||||||
{root === ROOT_INSIDE && !isMasterDetail ? <Stack.Screen name='InsideStack' component={InsideStack} /> : null}
|
{root === RootEnum.ROOT_INSIDE && !isMasterDetail ? <Stack.Screen name='InsideStack' component={InsideStack} /> : null}
|
||||||
{root === ROOT_SET_USERNAME ? <Stack.Screen name='SetUsernameStack' component={SetUsernameStack} /> : null}
|
{root === RootEnum.ROOT_SET_USERNAME ? <Stack.Screen name='SetUsernameStack' component={SetUsernameStack} /> : null}
|
||||||
</>
|
</>
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import { NativeModules } from 'react-native';
|
import { NativeModules } from 'react-native';
|
||||||
import Reactotron from 'reactotron-react-native';
|
import Reactotron from 'reactotron-react-native';
|
||||||
import { reactotronRedux } from 'reactotron-redux';
|
import { reactotronRedux } from 'reactotron-redux';
|
||||||
|
@ -7,7 +8,12 @@ import sagaPlugin from 'reactotron-redux-saga';
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
const scriptURL = NativeModules.SourceCode.scriptURL;
|
const scriptURL = NativeModules.SourceCode.scriptURL;
|
||||||
const scriptHostname = scriptURL.split('://')[1].split(':')[0];
|
const scriptHostname = scriptURL.split('://')[1].split(':')[0];
|
||||||
Reactotron.configure({ host: scriptHostname }).useReactNative().use(reactotronRedux()).use(sagaPlugin()).connect();
|
Reactotron.setAsyncStorageHandler(AsyncStorage)
|
||||||
|
.configure({ host: scriptHostname })
|
||||||
|
.useReactNative()
|
||||||
|
.use(reactotronRedux())
|
||||||
|
.use(sagaPlugin())
|
||||||
|
.connect();
|
||||||
// Running on android device
|
// Running on android device
|
||||||
// $ adb reverse tcp:9090 tcp:9090
|
// $ adb reverse tcp:9090 tcp:9090
|
||||||
Reactotron.clear();
|
Reactotron.clear();
|
||||||
|
|
|
@ -2,8 +2,8 @@ const REQUEST = 'REQUEST';
|
||||||
const SUCCESS = 'SUCCESS';
|
const SUCCESS = 'SUCCESS';
|
||||||
const FAILURE = 'FAILURE';
|
const FAILURE = 'FAILURE';
|
||||||
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
||||||
function createRequestTypes(base = {}, types = defaultTypes): Record<any, any> {
|
function createRequestTypes(base = {}, types = defaultTypes): Record<string, string> {
|
||||||
const res: Record<any, any> = {};
|
const res: Record<string, string> = {};
|
||||||
types.forEach(type => (res[type] = `${base}_${type}`));
|
types.forEach(type => (res[type] = `${base}_${type}`));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPE
|
||||||
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
||||||
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
|
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
|
||||||
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
|
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
|
||||||
export const SET_ACTIVE_USERS = 'SET_ACTIVE_USERS';
|
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'CLEAR']);
|
||||||
export const USERS_TYPING = createRequestTypes('USERS_TYPING', ['ADD', 'REMOVE', 'CLEAR']);
|
export const USERS_TYPING = createRequestTypes('USERS_TYPING', ['ADD', 'REMOVE', 'CLEAR']);
|
||||||
export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [
|
export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [
|
||||||
'SET_TOKEN',
|
'SET_TOKEN',
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
|
|
||||||
import { IActiveUsers } from '../reducers/activeUsers';
|
import { IActiveUsers } from '../reducers/activeUsers';
|
||||||
import { SET_ACTIVE_USERS } from './actionsTypes';
|
import { ACTIVE_USERS } from './actionsTypes';
|
||||||
|
|
||||||
export interface ISetActiveUsers extends Action {
|
interface ISetActiveUsers extends Action {
|
||||||
activeUsers: IActiveUsers;
|
activeUsers: IActiveUsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TActionActiveUsers = ISetActiveUsers;
|
export type TActionActiveUsers = ISetActiveUsers;
|
||||||
|
|
||||||
export const setActiveUsers = (activeUsers: IActiveUsers): ISetActiveUsers => ({
|
export const setActiveUsers = (activeUsers: IActiveUsers): ISetActiveUsers => ({
|
||||||
type: SET_ACTIVE_USERS,
|
type: ACTIVE_USERS.SET,
|
||||||
activeUsers
|
activeUsers
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearActiveUsers = (): Action => ({
|
||||||
|
type: ACTIVE_USERS.CLEAR
|
||||||
|
});
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { APP } from './actionsTypes';
|
|
||||||
|
|
||||||
export const ROOT_OUTSIDE = 'outside';
|
|
||||||
export const ROOT_INSIDE = 'inside';
|
|
||||||
export const ROOT_LOADING = 'loading';
|
|
||||||
export const ROOT_SET_USERNAME = 'setUsername';
|
|
||||||
|
|
||||||
export function appStart({ root, ...args }) {
|
|
||||||
return {
|
|
||||||
type: APP.START,
|
|
||||||
root,
|
|
||||||
...args
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appReady() {
|
|
||||||
return {
|
|
||||||
type: APP.READY
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appInit() {
|
|
||||||
return {
|
|
||||||
type: APP.INIT
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appInitLocalSettings() {
|
|
||||||
return {
|
|
||||||
type: APP.INIT_LOCAL_SETTINGS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setMasterDetail(isMasterDetail) {
|
|
||||||
return {
|
|
||||||
type: APP.SET_MASTER_DETAIL,
|
|
||||||
isMasterDetail
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { RootEnum } from '../definitions';
|
||||||
|
import { APP } from './actionsTypes';
|
||||||
|
|
||||||
|
interface IAppStart extends Action {
|
||||||
|
root: RootEnum;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISetMasterDetail extends Action {
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionApp = IAppStart & ISetMasterDetail;
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
root: RootEnum;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appStart({ root, ...args }: Params): IAppStart {
|
||||||
|
return {
|
||||||
|
type: APP.START,
|
||||||
|
root,
|
||||||
|
...args
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appReady(): Action {
|
||||||
|
return {
|
||||||
|
type: APP.READY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appInit(): Action {
|
||||||
|
return {
|
||||||
|
type: APP.INIT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appInitLocalSettings(): Action {
|
||||||
|
return {
|
||||||
|
type: APP.INIT_LOCAL_SETTINGS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setMasterDetail(isMasterDetail: boolean): ISetMasterDetail {
|
||||||
|
return {
|
||||||
|
type: APP.SET_MASTER_DETAIL,
|
||||||
|
isMasterDetail
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function connectRequest() {
|
|
||||||
return {
|
|
||||||
type: types.METEOR.REQUEST
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function connectSuccess() {
|
|
||||||
return {
|
|
||||||
type: types.METEOR.SUCCESS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function disconnect(err) {
|
|
||||||
return {
|
|
||||||
type: types.METEOR.DISCONNECT,
|
|
||||||
err
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
|
export function connectRequest(): Action {
|
||||||
|
return {
|
||||||
|
type: types.METEOR.REQUEST
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function connectSuccess(): Action {
|
||||||
|
return {
|
||||||
|
type: types.METEOR.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function disconnect(): Action {
|
||||||
|
return {
|
||||||
|
type: types.METEOR.DISCONNECT
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function createChannelRequest(data) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_CHANNEL.REQUEST,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createChannelSuccess(data) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_CHANNEL.SUCCESS,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createChannelFailure(err, isTeam) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_CHANNEL.FAILURE,
|
|
||||||
err,
|
|
||||||
isTeam
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { TCreateChannelResult } from '../reducers/createChannel';
|
||||||
|
import { CREATE_CHANNEL } from './actionsTypes';
|
||||||
|
|
||||||
|
interface ICreateChannelRequest extends Action {
|
||||||
|
data: TCreateChannelResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICreateChannelSuccess extends Action {
|
||||||
|
data: TCreateChannelResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICreateChannelFailure extends Action {
|
||||||
|
err: any;
|
||||||
|
isTeam: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionCreateChannel = ICreateChannelRequest & ICreateChannelSuccess & ICreateChannelFailure;
|
||||||
|
|
||||||
|
export function createChannelRequest(data: TCreateChannelResult): ICreateChannelRequest {
|
||||||
|
return {
|
||||||
|
type: CREATE_CHANNEL.REQUEST,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChannelSuccess(data: TCreateChannelResult): ICreateChannelSuccess {
|
||||||
|
return {
|
||||||
|
type: CREATE_CHANNEL.SUCCESS,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChannelFailure(err: any, isTeam: boolean): ICreateChannelFailure {
|
||||||
|
return {
|
||||||
|
type: CREATE_CHANNEL.FAILURE,
|
||||||
|
err,
|
||||||
|
isTeam
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function createDiscussionRequest(data) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_DISCUSSION.REQUEST,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDiscussionSuccess(data) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_DISCUSSION.SUCCESS,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDiscussionFailure(err) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_DISCUSSION.FAILURE,
|
|
||||||
err
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { CREATE_DISCUSSION } from './actionsTypes';
|
||||||
|
|
||||||
|
interface ICreateDiscussionRequest extends Action {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICreateDiscussionSuccess extends Action {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICreateDiscussionFailure extends Action {
|
||||||
|
err: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionCreateDiscussion = ICreateDiscussionRequest & ICreateDiscussionSuccess & ICreateDiscussionFailure;
|
||||||
|
|
||||||
|
export function createDiscussionRequest(data: any): ICreateDiscussionRequest {
|
||||||
|
return {
|
||||||
|
type: CREATE_DISCUSSION.REQUEST,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDiscussionSuccess(data: any): ICreateDiscussionSuccess {
|
||||||
|
return {
|
||||||
|
type: CREATE_DISCUSSION.SUCCESS,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDiscussionFailure(err: any): ICreateDiscussionFailure {
|
||||||
|
return {
|
||||||
|
type: CREATE_DISCUSSION.FAILURE,
|
||||||
|
err
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function setCustomEmojis(emojis) {
|
|
||||||
return {
|
|
||||||
type: types.SET_CUSTOM_EMOJIS,
|
|
||||||
emojis
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { ICustomEmojis } from '../reducers/customEmojis';
|
||||||
|
import { SET_CUSTOM_EMOJIS } from './actionsTypes';
|
||||||
|
|
||||||
|
export interface ISetCustomEmojis extends Action {
|
||||||
|
emojis: ICustomEmojis;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionCustomEmojis = ISetCustomEmojis;
|
||||||
|
|
||||||
|
export function setCustomEmojis(emojis: ICustomEmojis): ISetCustomEmojis {
|
||||||
|
return {
|
||||||
|
type: SET_CUSTOM_EMOJIS,
|
||||||
|
emojis
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function deepLinkingOpen(params) {
|
|
||||||
return {
|
|
||||||
type: types.DEEP_LINKING.OPEN,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { DEEP_LINKING } from './actionsTypes';
|
||||||
|
|
||||||
|
interface IParams {
|
||||||
|
path: string;
|
||||||
|
rid: string;
|
||||||
|
messageId: string;
|
||||||
|
host: string;
|
||||||
|
isCall: boolean;
|
||||||
|
fullURL: string;
|
||||||
|
type: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDeepLinkingOpen extends Action {
|
||||||
|
params: Partial<IParams>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deepLinkingOpen(params: Partial<IParams>): IDeepLinkingOpen {
|
||||||
|
return {
|
||||||
|
type: DEEP_LINKING.OPEN,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function encryptionInit() {
|
|
||||||
return {
|
|
||||||
type: types.ENCRYPTION.INIT
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encryptionStop() {
|
|
||||||
return {
|
|
||||||
type: types.ENCRYPTION.STOP
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encryptionSet(enabled = false, banner = null) {
|
|
||||||
return {
|
|
||||||
type: types.ENCRYPTION.SET,
|
|
||||||
enabled,
|
|
||||||
banner
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encryptionSetBanner(banner) {
|
|
||||||
return {
|
|
||||||
type: types.ENCRYPTION.SET_BANNER,
|
|
||||||
banner
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encryptionDecodeKey(password) {
|
|
||||||
return {
|
|
||||||
type: types.ENCRYPTION.DECODE_KEY,
|
|
||||||
password
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IBanner } from '../reducers/encryption';
|
||||||
|
import { ENCRYPTION } from './actionsTypes';
|
||||||
|
|
||||||
|
export interface IEncryptionSet extends Action {
|
||||||
|
enabled: boolean;
|
||||||
|
banner: IBanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEncryptionSetBanner extends Action {
|
||||||
|
banner: IBanner;
|
||||||
|
}
|
||||||
|
export interface IEncryptionDecodeKey extends Action {
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionEncryption = IEncryptionSet & IEncryptionSetBanner & IEncryptionDecodeKey;
|
||||||
|
|
||||||
|
export function encryptionInit(): Action {
|
||||||
|
return {
|
||||||
|
type: ENCRYPTION.INIT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encryptionStop(): Action {
|
||||||
|
return {
|
||||||
|
type: ENCRYPTION.STOP
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encryptionSet(enabled = false, banner: IBanner = ''): IEncryptionSet {
|
||||||
|
return {
|
||||||
|
type: ENCRYPTION.SET,
|
||||||
|
enabled,
|
||||||
|
banner
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encryptionSetBanner(banner: IBanner = ''): IEncryptionSetBanner {
|
||||||
|
return {
|
||||||
|
type: ENCRYPTION.SET_BANNER,
|
||||||
|
banner
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encryptionDecodeKey(password: string): IEncryptionDecodeKey {
|
||||||
|
return {
|
||||||
|
type: ENCRYPTION.DECODE_KEY,
|
||||||
|
password
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
import { ENTERPRISE_MODULES } from './actionsTypes';
|
|
||||||
|
|
||||||
export function setEnterpriseModules(modules) {
|
|
||||||
return {
|
|
||||||
type: ENTERPRISE_MODULES.SET,
|
|
||||||
payload: modules
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearEnterpriseModules() {
|
|
||||||
return {
|
|
||||||
type: ENTERPRISE_MODULES.CLEAR
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IEnterpriseModules } from '../reducers/enterpriseModules';
|
||||||
|
import { ENTERPRISE_MODULES } from './actionsTypes';
|
||||||
|
|
||||||
|
interface ISetEnterpriseModules extends Action {
|
||||||
|
payload: IEnterpriseModules[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionEnterpriseModules = ISetEnterpriseModules & Action;
|
||||||
|
|
||||||
|
export function setEnterpriseModules(modules: IEnterpriseModules[]): ISetEnterpriseModules {
|
||||||
|
return {
|
||||||
|
type: ENTERPRISE_MODULES.SET,
|
||||||
|
payload: modules
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearEnterpriseModules(): Action {
|
||||||
|
return {
|
||||||
|
type: ENTERPRISE_MODULES.CLEAR
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function inviteLinksSetToken(token) {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.SET_TOKEN,
|
|
||||||
token
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksRequest(token) {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.REQUEST,
|
|
||||||
token
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksSuccess() {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.SUCCESS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksFailure() {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.FAILURE
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksClear() {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.CLEAR
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksCreate(rid) {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.CREATE,
|
|
||||||
rid
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksSetParams(params) {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.SET_PARAMS,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inviteLinksSetInvite(invite) {
|
|
||||||
return {
|
|
||||||
type: types.INVITE_LINKS.SET_INVITE,
|
|
||||||
invite
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { TInvite } from '../reducers/inviteLinks';
|
||||||
|
import { INVITE_LINKS } from './actionsTypes';
|
||||||
|
|
||||||
|
interface IInviteLinksGeneric extends Action {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInviteLinksCreate extends Action {
|
||||||
|
rid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInviteLinksSetInvite extends Action {
|
||||||
|
invite: TInvite;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TParams = Record<string, any>;
|
||||||
|
|
||||||
|
interface IInviteLinksSetParams extends Action {
|
||||||
|
params: TParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionInviteLinks = IInviteLinksGeneric & IInviteLinksCreate & IInviteLinksSetInvite & IInviteLinksSetParams;
|
||||||
|
|
||||||
|
export const inviteLinksSetToken = (token: string): IInviteLinksGeneric => ({
|
||||||
|
type: INVITE_LINKS.SET_TOKEN,
|
||||||
|
token
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksRequest = (token: string): IInviteLinksGeneric => ({
|
||||||
|
type: INVITE_LINKS.REQUEST,
|
||||||
|
token
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksSuccess = (): Action => ({
|
||||||
|
type: INVITE_LINKS.SUCCESS
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksFailure = (): Action => ({
|
||||||
|
type: INVITE_LINKS.FAILURE
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksClear = (): Action => ({
|
||||||
|
type: INVITE_LINKS.CLEAR
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksCreate = (rid: string): IInviteLinksCreate => ({
|
||||||
|
type: INVITE_LINKS.CREATE,
|
||||||
|
rid
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksSetParams = (params: TParams): IInviteLinksSetParams => ({
|
||||||
|
type: INVITE_LINKS.SET_PARAMS,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
|
||||||
|
export const inviteLinksSetInvite = (invite: TInvite): IInviteLinksSetInvite => ({
|
||||||
|
type: INVITE_LINKS.SET_INVITE,
|
||||||
|
invite
|
||||||
|
});
|
|
@ -1,59 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function loginRequest(credentials, logoutOnError, isFromWebView) {
|
|
||||||
return {
|
|
||||||
type: types.LOGIN.REQUEST,
|
|
||||||
credentials,
|
|
||||||
logoutOnError,
|
|
||||||
isFromWebView
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loginSuccess(user) {
|
|
||||||
return {
|
|
||||||
type: types.LOGIN.SUCCESS,
|
|
||||||
user
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loginFailure(err) {
|
|
||||||
return {
|
|
||||||
type: types.LOGIN.FAILURE,
|
|
||||||
err
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function logout(forcedByServer = false) {
|
|
||||||
return {
|
|
||||||
type: types.LOGOUT,
|
|
||||||
forcedByServer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setUser(user) {
|
|
||||||
return {
|
|
||||||
type: types.USER.SET,
|
|
||||||
user
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setLoginServices(data) {
|
|
||||||
return {
|
|
||||||
type: types.LOGIN.SET_SERVICES,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPreference(preference) {
|
|
||||||
return {
|
|
||||||
type: types.LOGIN.SET_PREFERENCE,
|
|
||||||
preference
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setLocalAuthenticated(isLocalAuthenticated) {
|
|
||||||
return {
|
|
||||||
type: types.LOGIN.SET_LOCAL_AUTHENTICATED,
|
|
||||||
isLocalAuthenticated
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IUser } from '../definitions';
|
||||||
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
|
interface ICredentials {
|
||||||
|
resume: string;
|
||||||
|
user: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginRequest extends Action {
|
||||||
|
credentials: any;
|
||||||
|
logoutOnError?: boolean;
|
||||||
|
isFromWebView?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginSuccess extends Action {
|
||||||
|
user: Partial<IUser>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginFailure extends Action {
|
||||||
|
err: Partial<IUser>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILogout extends Action {
|
||||||
|
forcedByServer: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISetUser extends Action {
|
||||||
|
user: Partial<IUser>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISetServices extends Action {
|
||||||
|
data: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISetPreference extends Action {
|
||||||
|
preference: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISetLocalAuthenticated extends Action {
|
||||||
|
isLocalAuthenticated: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionsLogin = ILoginRequest &
|
||||||
|
ILoginSuccess &
|
||||||
|
ILoginFailure &
|
||||||
|
ILogout &
|
||||||
|
ISetUser &
|
||||||
|
ISetServices &
|
||||||
|
ISetPreference &
|
||||||
|
ISetLocalAuthenticated;
|
||||||
|
|
||||||
|
export function loginRequest(
|
||||||
|
credentials: Partial<ICredentials>,
|
||||||
|
logoutOnError?: boolean,
|
||||||
|
isFromWebView?: boolean
|
||||||
|
): ILoginRequest {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.REQUEST,
|
||||||
|
credentials,
|
||||||
|
logoutOnError,
|
||||||
|
isFromWebView
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loginSuccess(user: Partial<IUser>): ILoginSuccess {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.SUCCESS,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loginFailure(err: Record<string, any>): ILoginFailure {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.FAILURE,
|
||||||
|
err
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logout(forcedByServer = false): ILogout {
|
||||||
|
return {
|
||||||
|
type: types.LOGOUT,
|
||||||
|
forcedByServer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setUser(user: Partial<IUser>): ISetUser {
|
||||||
|
return {
|
||||||
|
type: types.USER.SET,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLoginServices(data: Record<string, any>): ISetServices {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.SET_SERVICES,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPreference(preference: Record<string, any>): ISetPreference {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.SET_PREFERENCE,
|
||||||
|
preference
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLocalAuthenticated(isLocalAuthenticated: boolean): ISetLocalAuthenticated {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.SET_LOCAL_AUTHENTICATED,
|
||||||
|
isLocalAuthenticated
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function replyBroadcast(message) {
|
|
||||||
return {
|
|
||||||
type: types.MESSAGES.REPLY_BROADCAST,
|
|
||||||
message
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { MESSAGES } from './actionsTypes';
|
||||||
|
|
||||||
|
type IMessage = Record<string, string>;
|
||||||
|
|
||||||
|
interface IReplyBroadcast extends Action {
|
||||||
|
message: IMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replyBroadcast(message: IMessage): IReplyBroadcast {
|
||||||
|
return {
|
||||||
|
type: MESSAGES.REPLY_BROADCAST,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function setPermissions(permissions) {
|
|
||||||
return {
|
|
||||||
type: types.PERMISSIONS.SET,
|
|
||||||
permissions
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updatePermission(id, roles) {
|
|
||||||
return {
|
|
||||||
type: types.PERMISSIONS.UPDATE,
|
|
||||||
payload: { id, roles }
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IPermissionsState, TSupportedPermissions } from '../reducers/permissions';
|
||||||
|
import { PERMISSIONS } from './actionsTypes';
|
||||||
|
|
||||||
|
interface ISetPermissions extends Action {
|
||||||
|
permissions: IPermissionsState;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUpdatePermissions extends Action {
|
||||||
|
payload: { id: TSupportedPermissions; roles: string[] };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionPermissions = ISetPermissions & IUpdatePermissions;
|
||||||
|
|
||||||
|
export function setPermissions(permissions: IPermissionsState): ISetPermissions {
|
||||||
|
return {
|
||||||
|
type: PERMISSIONS.SET,
|
||||||
|
permissions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updatePermission(id: TSupportedPermissions, roles: string[]): IUpdatePermissions {
|
||||||
|
return {
|
||||||
|
type: PERMISSIONS.UPDATE,
|
||||||
|
payload: { id, roles }
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function setRoles(roles) {
|
|
||||||
return {
|
|
||||||
type: types.ROLES.SET,
|
|
||||||
roles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export function updateRoles(id, desc) {
|
|
||||||
return {
|
|
||||||
type: types.ROLES.UPDATE,
|
|
||||||
payload: { id, desc }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export function removeRoles(id) {
|
|
||||||
return {
|
|
||||||
type: types.ROLES.REMOVE,
|
|
||||||
payload: { id }
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IRoles } from '../reducers/roles';
|
||||||
|
import { ROLES } from './actionsTypes';
|
||||||
|
|
||||||
|
export interface ISetRoles extends Action {
|
||||||
|
roles: IRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUpdateRoles extends Action {
|
||||||
|
payload: { id: string; desc: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRemoveRoles extends Action {
|
||||||
|
payload: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IActionRoles = ISetRoles & IUpdateRoles & IRemoveRoles;
|
||||||
|
|
||||||
|
export function setRoles(roles: IRoles): ISetRoles {
|
||||||
|
return {
|
||||||
|
type: ROLES.SET,
|
||||||
|
roles
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateRoles(id: string, desc: string): IUpdateRoles {
|
||||||
|
return {
|
||||||
|
type: ROLES.UPDATE,
|
||||||
|
payload: { id, desc }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeRoles(id: string): IRemoveRoles {
|
||||||
|
return {
|
||||||
|
type: ROLES.REMOVE,
|
||||||
|
payload: { id }
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function subscribeRoom(rid) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.SUBSCRIBE,
|
|
||||||
rid
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unsubscribeRoom(rid) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.UNSUBSCRIBE,
|
|
||||||
rid
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function leaveRoom(roomType, room, selected) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.LEAVE,
|
|
||||||
room,
|
|
||||||
roomType,
|
|
||||||
selected
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteRoom(roomType, room, selected) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.DELETE,
|
|
||||||
room,
|
|
||||||
roomType,
|
|
||||||
selected
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function closeRoom(rid) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.CLOSE,
|
|
||||||
rid
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function forwardRoom(rid, transferData) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.FORWARD,
|
|
||||||
transferData,
|
|
||||||
rid
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removedRoom() {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.REMOVED
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function userTyping(rid, status = true) {
|
|
||||||
return {
|
|
||||||
type: types.ROOM.USER_TYPING,
|
|
||||||
rid,
|
|
||||||
status
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { ERoomType } from '../definitions/ERoomType';
|
||||||
|
import { ROOM } from './actionsTypes';
|
||||||
|
|
||||||
|
// TYPE RETURN RELATED
|
||||||
|
type ISelected = Record<string, string>;
|
||||||
|
|
||||||
|
export interface ITransferData {
|
||||||
|
roomId: string;
|
||||||
|
userId?: string;
|
||||||
|
departmentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTION RETURN RELATED
|
||||||
|
interface IBaseReturn extends Action {
|
||||||
|
rid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TSubscribeRoom = IBaseReturn;
|
||||||
|
type TUnsubscribeRoom = IBaseReturn;
|
||||||
|
type TCloseRoom = IBaseReturn;
|
||||||
|
|
||||||
|
type TRoom = Record<string, any>;
|
||||||
|
|
||||||
|
interface ILeaveRoom extends Action {
|
||||||
|
roomType: ERoomType;
|
||||||
|
room: TRoom;
|
||||||
|
selected?: ISelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDeleteRoom extends Action {
|
||||||
|
roomType: ERoomType;
|
||||||
|
room: TRoom;
|
||||||
|
selected?: ISelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IForwardRoom extends Action {
|
||||||
|
transferData: ITransferData;
|
||||||
|
rid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUserTyping extends Action {
|
||||||
|
rid: string;
|
||||||
|
status: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionsRoom = TSubscribeRoom & TUnsubscribeRoom & TCloseRoom & ILeaveRoom & IDeleteRoom & IForwardRoom & IUserTyping;
|
||||||
|
|
||||||
|
export function subscribeRoom(rid: string): TSubscribeRoom {
|
||||||
|
return {
|
||||||
|
type: ROOM.SUBSCRIBE,
|
||||||
|
rid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unsubscribeRoom(rid: string): TUnsubscribeRoom {
|
||||||
|
return {
|
||||||
|
type: ROOM.UNSUBSCRIBE,
|
||||||
|
rid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function leaveRoom(roomType: ERoomType, room: TRoom, selected?: ISelected): ILeaveRoom {
|
||||||
|
return {
|
||||||
|
type: ROOM.LEAVE,
|
||||||
|
room,
|
||||||
|
roomType,
|
||||||
|
selected
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteRoom(roomType: ERoomType, room: TRoom, selected?: ISelected): IDeleteRoom {
|
||||||
|
return {
|
||||||
|
type: ROOM.DELETE,
|
||||||
|
room,
|
||||||
|
roomType,
|
||||||
|
selected
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeRoom(rid: string): TCloseRoom {
|
||||||
|
return {
|
||||||
|
type: ROOM.CLOSE,
|
||||||
|
rid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function forwardRoom(rid: string, transferData: ITransferData): IForwardRoom {
|
||||||
|
return {
|
||||||
|
type: ROOM.FORWARD,
|
||||||
|
transferData,
|
||||||
|
rid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removedRoom(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOM.REMOVED
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function userTyping(rid: string, status = true): IUserTyping {
|
||||||
|
return {
|
||||||
|
type: ROOM.USER_TYPING,
|
||||||
|
rid,
|
||||||
|
status
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function roomsRequest(params = { allData: false }) {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.REQUEST,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function roomsSuccess() {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.SUCCESS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function roomsFailure(err) {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.FAILURE,
|
|
||||||
err
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function roomsRefresh() {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.REFRESH
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setSearch(searchText) {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.SET_SEARCH,
|
|
||||||
searchText
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function closeServerDropdown() {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.CLOSE_SERVER_DROPDOWN
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toggleServerDropdown() {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.TOGGLE_SERVER_DROPDOWN
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openSearchHeader() {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.OPEN_SEARCH_HEADER
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function closeSearchHeader() {
|
|
||||||
return {
|
|
||||||
type: types.ROOMS.CLOSE_SEARCH_HEADER
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { ROOMS } from './actionsTypes';
|
||||||
|
|
||||||
|
export interface IRoomsRequest extends Action {
|
||||||
|
params: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetSearch extends Action {
|
||||||
|
searchText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoomsFailure extends Action {
|
||||||
|
err: Record<string, any> | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IRoomsAction = IRoomsRequest & ISetSearch & IRoomsFailure;
|
||||||
|
|
||||||
|
export function roomsRequest(
|
||||||
|
params: {
|
||||||
|
allData: boolean;
|
||||||
|
} = { allData: false }
|
||||||
|
): IRoomsRequest {
|
||||||
|
return {
|
||||||
|
type: ROOMS.REQUEST,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function roomsSuccess(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOMS.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function roomsFailure(err: string): IRoomsFailure {
|
||||||
|
return {
|
||||||
|
type: ROOMS.FAILURE,
|
||||||
|
err
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function roomsRefresh(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOMS.REFRESH
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSearch(searchText: string): ISetSearch {
|
||||||
|
return {
|
||||||
|
type: ROOMS.SET_SEARCH,
|
||||||
|
searchText
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeServerDropdown(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOMS.CLOSE_SERVER_DROPDOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleServerDropdown(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOMS.TOGGLE_SERVER_DROPDOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openSearchHeader(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOMS.OPEN_SEARCH_HEADER
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeSearchHeader(): Action {
|
||||||
|
return {
|
||||||
|
type: ROOMS.CLOSE_SEARCH_HEADER
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
import { SERVER } from './actionsTypes';
|
|
||||||
|
|
||||||
export function selectServerRequest(server, version, fetchVersion = true, changeServer = false) {
|
|
||||||
return {
|
|
||||||
type: SERVER.SELECT_REQUEST,
|
|
||||||
server,
|
|
||||||
version,
|
|
||||||
fetchVersion,
|
|
||||||
changeServer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectServerSuccess(server, version) {
|
|
||||||
return {
|
|
||||||
type: SERVER.SELECT_SUCCESS,
|
|
||||||
server,
|
|
||||||
version
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectServerFailure() {
|
|
||||||
return {
|
|
||||||
type: SERVER.SELECT_FAILURE
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serverRequest(server, username = null, fromServerHistory = false) {
|
|
||||||
return {
|
|
||||||
type: SERVER.REQUEST,
|
|
||||||
server,
|
|
||||||
username,
|
|
||||||
fromServerHistory
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serverSuccess() {
|
|
||||||
return {
|
|
||||||
type: SERVER.SUCCESS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serverFailure(err) {
|
|
||||||
return {
|
|
||||||
type: SERVER.FAILURE,
|
|
||||||
err
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serverInitAdd(previousServer) {
|
|
||||||
return {
|
|
||||||
type: SERVER.INIT_ADD,
|
|
||||||
previousServer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serverFinishAdd() {
|
|
||||||
return {
|
|
||||||
type: SERVER.FINISH_ADD
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { SERVER } from './actionsTypes';
|
||||||
|
|
||||||
|
interface ISelectServer extends Action {
|
||||||
|
server: string;
|
||||||
|
version?: string;
|
||||||
|
fetchVersion: boolean;
|
||||||
|
changeServer: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISelectServerSuccess extends Action {
|
||||||
|
server: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IServer extends Action {
|
||||||
|
server: string;
|
||||||
|
username: string | null;
|
||||||
|
fromServerHistory: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IServerInit extends Action {
|
||||||
|
previousServer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IServerFailure extends Action {
|
||||||
|
err: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionServer = ISelectServer & ISelectServerSuccess & IServer & IServerInit & IServerFailure;
|
||||||
|
|
||||||
|
export function selectServerRequest(server: string, version?: string, fetchVersion = true, changeServer = false): ISelectServer {
|
||||||
|
return {
|
||||||
|
type: SERVER.SELECT_REQUEST,
|
||||||
|
server,
|
||||||
|
version,
|
||||||
|
fetchVersion,
|
||||||
|
changeServer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectServerSuccess(server: string, version: string): ISelectServerSuccess {
|
||||||
|
return {
|
||||||
|
type: SERVER.SELECT_SUCCESS,
|
||||||
|
server,
|
||||||
|
version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectServerFailure(): Action {
|
||||||
|
return {
|
||||||
|
type: SERVER.SELECT_FAILURE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serverRequest(server: string, username: string | null = null, fromServerHistory = false): IServer {
|
||||||
|
return {
|
||||||
|
type: SERVER.REQUEST,
|
||||||
|
server,
|
||||||
|
username,
|
||||||
|
fromServerHistory
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serverSuccess(): Action {
|
||||||
|
return {
|
||||||
|
type: SERVER.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serverFailure(err: any): IServerFailure {
|
||||||
|
return {
|
||||||
|
type: SERVER.FAILURE,
|
||||||
|
err
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serverInitAdd(previousServer: string): IServerInit {
|
||||||
|
return {
|
||||||
|
type: SERVER.INIT_ADD,
|
||||||
|
previousServer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serverFinishAdd(): Action {
|
||||||
|
return {
|
||||||
|
type: SERVER.FINISH_ADD
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
import { SETTINGS } from './actionsTypes';
|
|
||||||
|
|
||||||
export function addSettings(settings) {
|
|
||||||
return {
|
|
||||||
type: SETTINGS.ADD,
|
|
||||||
payload: settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSettings(id, value) {
|
|
||||||
return {
|
|
||||||
type: SETTINGS.UPDATE,
|
|
||||||
payload: { id, value }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearSettings() {
|
|
||||||
return {
|
|
||||||
type: SETTINGS.CLEAR
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { ISettings, TSettings } from '../reducers/settings';
|
||||||
|
import { SETTINGS } from './actionsTypes';
|
||||||
|
|
||||||
|
interface IAddSettings extends Action {
|
||||||
|
payload: ISettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUpdateSettings extends Action {
|
||||||
|
payload: { id: string; value: TSettings };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IActionSettings = IAddSettings & IUpdateSettings;
|
||||||
|
|
||||||
|
export function addSettings(settings: ISettings): IAddSettings {
|
||||||
|
return {
|
||||||
|
type: SETTINGS.ADD,
|
||||||
|
payload: settings
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateSettings(id: string, value: TSettings): IUpdateSettings {
|
||||||
|
return {
|
||||||
|
type: SETTINGS.UPDATE,
|
||||||
|
payload: { id, value }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearSettings(): Action {
|
||||||
|
return {
|
||||||
|
type: SETTINGS.CLEAR
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
import { SHARE } from './actionsTypes';
|
|
||||||
|
|
||||||
export function shareSelectServer(server) {
|
|
||||||
return {
|
|
||||||
type: SHARE.SELECT_SERVER,
|
|
||||||
server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function shareSetSettings(settings) {
|
|
||||||
return {
|
|
||||||
type: SHARE.SET_SETTINGS,
|
|
||||||
settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function shareSetUser(user) {
|
|
||||||
return {
|
|
||||||
type: SHARE.SET_USER,
|
|
||||||
user
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IShareServer, IShareUser, TShareSettings } from '../reducers/share';
|
||||||
|
import { SHARE } from './actionsTypes';
|
||||||
|
|
||||||
|
interface IShareSelectServer extends Action {
|
||||||
|
server: IShareServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IShareSetSettings extends Action {
|
||||||
|
settings: TShareSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IShareSetUser extends Action {
|
||||||
|
user: IShareUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionsShare = IShareSelectServer & IShareSetSettings & IShareSetUser;
|
||||||
|
|
||||||
|
export function shareSelectServer(server: IShareServer): IShareSelectServer {
|
||||||
|
return {
|
||||||
|
type: SHARE.SELECT_SERVER,
|
||||||
|
server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shareSetSettings(settings: TShareSettings): IShareSetSettings {
|
||||||
|
return {
|
||||||
|
type: SHARE.SET_SETTINGS,
|
||||||
|
settings
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shareSetUser(user: IShareUser): IShareSetUser {
|
||||||
|
return {
|
||||||
|
type: SHARE.SET_USER,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function setAllPreferences(preferences) {
|
|
||||||
return {
|
|
||||||
type: types.SORT_PREFERENCES.SET_ALL,
|
|
||||||
preferences
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPreference(preference) {
|
|
||||||
return {
|
|
||||||
type: types.SORT_PREFERENCES.SET,
|
|
||||||
preference
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IPreferences } from '../definitions';
|
||||||
|
import { SORT_PREFERENCES } from './actionsTypes';
|
||||||
|
|
||||||
|
interface ISetAllPreferences extends Action {
|
||||||
|
preferences: IPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISetPreference extends Action {
|
||||||
|
preference: Partial<IPreferences>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionSortPreferences = ISetAllPreferences & ISetPreference;
|
||||||
|
|
||||||
|
export function setAllPreferences(preferences: IPreferences): ISetAllPreferences {
|
||||||
|
return {
|
||||||
|
type: SORT_PREFERENCES.SET_ALL,
|
||||||
|
preferences
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPreference(preference: Partial<IPreferences>): ISetPreference {
|
||||||
|
return {
|
||||||
|
type: SORT_PREFERENCES.SET,
|
||||||
|
preference
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
import { USERS_TYPING } from './actionsTypes';
|
|
||||||
|
|
||||||
export function addUserTyping(username) {
|
|
||||||
return {
|
|
||||||
type: USERS_TYPING.ADD,
|
|
||||||
username
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeUserTyping(username) {
|
|
||||||
return {
|
|
||||||
type: USERS_TYPING.REMOVE,
|
|
||||||
username
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearUserTyping() {
|
|
||||||
return {
|
|
||||||
type: USERS_TYPING.CLEAR
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { USERS_TYPING } from './actionsTypes';
|
||||||
|
|
||||||
|
export interface IUsersTypingGenericAction extends Action {
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionUserTyping = IUsersTypingGenericAction & Action;
|
||||||
|
|
||||||
|
export function addUserTyping(username: string): IUsersTypingGenericAction {
|
||||||
|
return {
|
||||||
|
type: USERS_TYPING.ADD,
|
||||||
|
username
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeUserTyping(username: string): IUsersTypingGenericAction {
|
||||||
|
return {
|
||||||
|
type: USERS_TYPING.REMOVE,
|
||||||
|
username
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearUserTyping(): Action {
|
||||||
|
return {
|
||||||
|
type: USERS_TYPING.CLEAR
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/* eslint-disable no-bitwise */
|
/* eslint-disable no-bitwise */
|
||||||
import KeyCommands, { constants } from 'react-native-keycommands';
|
import { NativeSyntheticEvent } from 'react-native';
|
||||||
|
import KeyCommands, { constants, KeyCommand } from 'react-native-keycommands';
|
||||||
|
|
||||||
import I18n from './i18n';
|
import I18n from './i18n';
|
||||||
|
|
||||||
|
@ -136,13 +137,18 @@ const keyCommands = [
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
|
||||||
export const setKeyCommands = () => KeyCommands.setKeyCommands(keyCommands);
|
export const setKeyCommands = (): void => KeyCommands.setKeyCommands(keyCommands);
|
||||||
|
|
||||||
export const deleteKeyCommands = () => KeyCommands.deleteKeyCommands(keyCommands);
|
export const deleteKeyCommands = (): void => KeyCommands.deleteKeyCommands(keyCommands);
|
||||||
|
|
||||||
export const KEY_COMMAND = 'KEY_COMMAND';
|
export const KEY_COMMAND = 'KEY_COMMAND';
|
||||||
|
|
||||||
export const commandHandle = (event, key, flags = []) => {
|
interface IKeyCommandEvent extends NativeSyntheticEvent<typeof KeyCommand> {
|
||||||
|
input: string;
|
||||||
|
modifierFlags: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const commandHandle = (event: IKeyCommandEvent, key: string | string[], flags: string[] = []): boolean => {
|
||||||
const { input, modifierFlags } = event;
|
const { input, modifierFlags } = event;
|
||||||
let _flags = 0;
|
let _flags = 0;
|
||||||
if (flags.includes('command') && flags.includes('alternate')) {
|
if (flags.includes('command') && flags.includes('alternate')) {
|
||||||
|
@ -155,35 +161,41 @@ export const commandHandle = (event, key, flags = []) => {
|
||||||
return key.includes(input) && modifierFlags === _flags;
|
return key.includes(input) && modifierFlags === _flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleCommandTyping = event => commandHandle(event, KEY_TYPING);
|
export const handleCommandTyping = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_TYPING);
|
||||||
|
|
||||||
export const handleCommandSubmit = event => commandHandle(event, KEY_SEND_MESSAGE);
|
export const handleCommandSubmit = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_SEND_MESSAGE);
|
||||||
|
|
||||||
export const handleCommandShowUpload = event => commandHandle(event, KEY_UPLOAD, ['command']);
|
export const handleCommandShowUpload = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_UPLOAD, ['command']);
|
||||||
|
|
||||||
export const handleCommandScroll = event =>
|
export const handleCommandScroll = (event: IKeyCommandEvent): boolean =>
|
||||||
commandHandle(event, [constants.keyInputUpArrow, constants.keyInputDownArrow], ['alternate']);
|
commandHandle(event, [constants.keyInputUpArrow, constants.keyInputDownArrow], ['alternate']);
|
||||||
|
|
||||||
export const handleCommandRoomActions = event => commandHandle(event, KEY_ROOM_ACTIONS, ['command']);
|
export const handleCommandRoomActions = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_ROOM_ACTIONS, ['command']);
|
||||||
|
|
||||||
export const handleCommandSearchMessages = event => commandHandle(event, KEY_SEARCH, ['command']);
|
export const handleCommandSearchMessages = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_SEARCH, ['command']);
|
||||||
|
|
||||||
export const handleCommandReplyLatest = event => commandHandle(event, KEY_REPLY, ['command']);
|
export const handleCommandReplyLatest = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_REPLY, ['command']);
|
||||||
|
|
||||||
export const handleCommandSelectServer = event => commandHandle(event, KEY_SELECT, ['command', 'alternate']);
|
export const handleCommandSelectServer = (event: IKeyCommandEvent): boolean =>
|
||||||
|
commandHandle(event, KEY_SELECT, ['command', 'alternate']);
|
||||||
|
|
||||||
export const handleCommandShowPreferences = event => commandHandle(event, KEY_PREFERENCES, ['command']);
|
export const handleCommandShowPreferences = (event: IKeyCommandEvent): boolean =>
|
||||||
|
commandHandle(event, KEY_PREFERENCES, ['command']);
|
||||||
|
|
||||||
export const handleCommandSearching = event => commandHandle(event, KEY_SEARCH, ['command', 'alternate']);
|
export const handleCommandSearching = (event: IKeyCommandEvent): boolean =>
|
||||||
|
commandHandle(event, KEY_SEARCH, ['command', 'alternate']);
|
||||||
|
|
||||||
export const handleCommandSelectRoom = event => commandHandle(event, KEY_SELECT, ['command']);
|
export const handleCommandSelectRoom = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_SELECT, ['command']);
|
||||||
|
|
||||||
export const handleCommandPreviousRoom = event => commandHandle(event, KEY_PREVIOUS_ROOM, ['command']);
|
export const handleCommandPreviousRoom = (event: IKeyCommandEvent): boolean =>
|
||||||
|
commandHandle(event, KEY_PREVIOUS_ROOM, ['command']);
|
||||||
|
|
||||||
export const handleCommandNextRoom = event => commandHandle(event, KEY_NEXT_ROOM, ['command']);
|
export const handleCommandNextRoom = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_NEXT_ROOM, ['command']);
|
||||||
|
|
||||||
export const handleCommandShowNewMessage = event => commandHandle(event, KEY_NEW_ROOM, ['command']);
|
export const handleCommandShowNewMessage = (event: IKeyCommandEvent): boolean => commandHandle(event, KEY_NEW_ROOM, ['command']);
|
||||||
|
|
||||||
export const handleCommandAddNewServer = event => commandHandle(event, KEY_ADD_SERVER, ['command', 'alternate']);
|
export const handleCommandAddNewServer = (event: IKeyCommandEvent): boolean =>
|
||||||
|
commandHandle(event, KEY_ADD_SERVER, ['command', 'alternate']);
|
||||||
|
|
||||||
export const handleCommandOpenServerDropdown = event => commandHandle(event, KEY_SERVER_SELECTION, ['command', 'alternate']);
|
export const handleCommandOpenServerDropdown = (event: IKeyCommandEvent): boolean =>
|
||||||
|
commandHandle(event, KEY_SERVER_SELECTION, ['command', 'alternate']);
|
|
@ -1,6 +1,7 @@
|
||||||
export const PASSCODE_KEY = 'kPasscode';
|
export const PASSCODE_KEY = 'kPasscode';
|
||||||
export const LOCKED_OUT_TIMER_KEY = 'kLockedOutTimer';
|
export const LOCKED_OUT_TIMER_KEY = 'kLockedOutTimer';
|
||||||
export const ATTEMPTS_KEY = 'kAttempts';
|
export const ATTEMPTS_KEY = 'kAttempts';
|
||||||
|
export const BIOMETRY_ENABLED_KEY = 'kBiometryEnabled';
|
||||||
|
|
||||||
export const LOCAL_AUTHENTICATE_EMITTER = 'LOCAL_AUTHENTICATE';
|
export const LOCAL_AUTHENTICATE_EMITTER = 'LOCAL_AUTHENTICATE';
|
||||||
export const CHANGE_PASSCODE_EMITTER = 'CHANGE_PASSCODE';
|
export const CHANGE_PASSCODE_EMITTER = 'CHANGE_PASSCODE';
|
||||||
|
|
|
@ -44,7 +44,7 @@ const Avatar = React.memo(
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
image = (
|
image = (
|
||||||
<Emoji
|
<Emoji
|
||||||
theme={theme}
|
theme={theme!}
|
||||||
baseUrl={server}
|
baseUrl={server}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
isMessageContainsOnlyEmoji
|
isMessageContainsOnlyEmoji
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import { TSubscriptionModel, TUserModel } from '../../definitions';
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import { IAvatar } from './interfaces';
|
import { IAvatar } from './interfaces';
|
||||||
|
|
||||||
class AvatarContainer extends React.Component<IAvatar, any> {
|
class AvatarContainer extends React.Component<IAvatar, any> {
|
||||||
private mounted: boolean;
|
private mounted: boolean;
|
||||||
|
|
||||||
private subscription: any;
|
private subscription?: Subscription;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
text: '',
|
text: '',
|
||||||
|
@ -59,15 +61,17 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
||||||
record = user;
|
record = user;
|
||||||
} else {
|
} else {
|
||||||
const { rid } = this.props;
|
const { rid } = this.props;
|
||||||
record = await subsCollection.find(rid);
|
if (rid) {
|
||||||
|
record = await subsCollection.find(rid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Record not found
|
// Record not found
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record) {
|
if (record) {
|
||||||
const observable = record.observe();
|
const observable = record.observe() as Observable<TSubscriptionModel | TUserModel>;
|
||||||
this.subscription = observable.subscribe((r: any) => {
|
this.subscription = observable.subscribe(r => {
|
||||||
const { avatarETag } = r;
|
const { avatarETag } = r;
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ avatarETag });
|
this.setState({ avatarETag });
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
export interface IAvatar {
|
export interface IAvatar {
|
||||||
server?: string;
|
server?: string;
|
||||||
style?: any;
|
style?: any;
|
||||||
|
@ -14,10 +16,10 @@ export interface IAvatar {
|
||||||
};
|
};
|
||||||
theme?: string;
|
theme?: string;
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
getCustomEmoji?: () => any;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
avatarETag?: string;
|
avatarETag?: string;
|
||||||
isStatic?: boolean | string;
|
isStatic?: boolean | string;
|
||||||
rid?: string;
|
rid?: string;
|
||||||
blockUnauthenticatedAccess?: boolean;
|
blockUnauthenticatedAccess?: boolean;
|
||||||
serverVersion?: string;
|
serverVersion: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer basic 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer black theme - loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_black\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#f9f9f9\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"}},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer black theme - text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_black\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#f9f9f9\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer dark theme - loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_dark\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#f9f9f9\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"}},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer dark theme - text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_dark\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#f9f9f9\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#6C727A\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"}},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer long text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots BackgroundContainer text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Text here\\"]}]}"`;
|
|
@ -35,7 +35,7 @@ const styles = StyleSheet.create({
|
||||||
const BackgroundContainer = ({ theme, text, loading }: IBackgroundContainer) => (
|
const BackgroundContainer = ({ theme, text, loading }: IBackgroundContainer) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} />
|
<ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} />
|
||||||
{text ? <Text style={[styles.text, { color: themes[theme!].auxiliaryTintColor }]}>{text}</Text> : null}
|
{text && !loading ? <Text style={[styles.text, { color: themes[theme!].auxiliaryTintColor }]}>{text}</Text> : null}
|
||||||
{loading ? <ActivityIndicator style={styles.text} color={themes[theme!].auxiliaryTintColor} /> : null}
|
{loading ? <ActivityIndicator style={styles.text} color={themes[theme!].auxiliaryTintColor} /> : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
|
||||||
import { ICustomEmoji } from './interfaces';
|
import { ICustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const CustomEmoji = React.memo(
|
const CustomEmoji = React.memo(
|
||||||
({ baseUrl, emoji, style }: ICustomEmoji) => (
|
({ baseUrl, emoji, style }: ICustomEmoji) => (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import CustomEmoji from './CustomEmoji';
|
import CustomEmoji from './CustomEmoji';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
import { IEmoji, IEmojiCategory } from './interfaces';
|
import { IEmoji, IEmojiCategory } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const EMOJI_SIZE = 50;
|
const EMOJI_SIZE = 50;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { IEmoji } from './interfaces';
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const scrollProps = {
|
const scrollProps = {
|
||||||
keyboardShouldPersistTaps: 'always',
|
keyboardShouldPersistTaps: 'always',
|
||||||
|
@ -36,7 +36,7 @@ interface IEmojiPickerProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IEmojiPickerState {
|
interface IEmojiPickerState {
|
||||||
frequentlyUsed: [];
|
frequentlyUsed: (string | { content?: string; extension?: string; isCustom: boolean })[];
|
||||||
customEmojis: any;
|
customEmojis: any;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
width: number | null;
|
width: number | null;
|
||||||
|
@ -114,7 +114,7 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.action(async () => {
|
await db.write(async () => {
|
||||||
if (freqEmojiRecord) {
|
if (freqEmojiRecord) {
|
||||||
await freqEmojiRecord.update((f: any) => {
|
await freqEmojiRecord.update((f: any) => {
|
||||||
f.count += 1;
|
f.count += 1;
|
||||||
|
@ -132,8 +132,8 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
||||||
updateFrequentlyUsed = async () => {
|
updateFrequentlyUsed = async () => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
||||||
let frequentlyUsed: any = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
||||||
frequentlyUsed = frequentlyUsed.map((item: IEmoji) => {
|
const frequentlyUsed = frequentlyUsedOrdered.map(item => {
|
||||||
if (item.isCustom) {
|
if (item.isCustom) {
|
||||||
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import database from '../../lib/database';
|
||||||
import { Button } from '../ActionSheet';
|
import { Button } from '../ActionSheet';
|
||||||
import { useDimensions } from '../../dimensions';
|
import { useDimensions } from '../../dimensions';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { IEmoji } from '../EmojiPicker/interfaces';
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
|
||||||
|
|
||||||
interface IHeader {
|
interface IHeader {
|
||||||
handleReaction: Function;
|
handleReaction: Function;
|
||||||
|
@ -90,14 +91,14 @@ const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
|
||||||
));
|
));
|
||||||
|
|
||||||
const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => {
|
const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => {
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState<(TFrequentlyUsedEmojiModel | string)[]>([]);
|
||||||
const { width, height }: any = useDimensions();
|
const { width, height }: any = useDimensions();
|
||||||
|
|
||||||
const setEmojis = async () => {
|
const setEmojis = async () => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||||
let freqEmojis = await freqEmojiCollection.query().fetch();
|
let freqEmojis: (TFrequentlyUsedEmojiModel | string)[] = await freqEmojiCollection.query().fetch();
|
||||||
|
|
||||||
const isLandscape = width > height;
|
const isLandscape = width > height;
|
||||||
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { showConfirmationAlert } from '../../utils/info';
|
||||||
import { useActionSheet } from '../ActionSheet';
|
import { useActionSheet } from '../ActionSheet';
|
||||||
import Header, { HEADER_HEIGHT } from './Header';
|
import Header, { HEADER_HEIGHT } from './Header';
|
||||||
import events from '../../utils/log/events';
|
import events from '../../utils/log/events';
|
||||||
|
import { TMessageModel } from '../../definitions/IMessage';
|
||||||
|
|
||||||
interface IMessageActions {
|
interface IMessageActions {
|
||||||
room: {
|
room: {
|
||||||
|
@ -182,9 +183,9 @@ const MessageActions = React.memo(
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const subCollection = db.get('subscriptions');
|
const subCollection = db.get('subscriptions');
|
||||||
const subRecord = await subCollection.find(rid);
|
const subRecord = await subCollection.find(rid);
|
||||||
await db.action(async () => {
|
await db.write(async () => {
|
||||||
try {
|
try {
|
||||||
await subRecord.update((sub: any) => (sub.lastOpen = ts));
|
await subRecord.update(sub => (sub.lastOpen = ts));
|
||||||
} catch {
|
} catch {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -269,11 +270,11 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleTranslation = async (message: any) => {
|
const handleToggleTranslation = async (message: TMessageModel) => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.action(async () => {
|
await db.write(async () => {
|
||||||
await message.update((m: any) => {
|
await message.update(m => {
|
||||||
m.autoTranslate = !m.autoTranslate;
|
m.autoTranslate = !m.autoTranslate;
|
||||||
m._updatedAt = new Date();
|
m._updatedAt = new Date();
|
||||||
});
|
});
|
||||||
|
@ -282,7 +283,7 @@ const MessageActions = React.memo(
|
||||||
if (!translatedMessage) {
|
if (!translatedMessage) {
|
||||||
const m = {
|
const m = {
|
||||||
_id: message.id,
|
_id: message.id,
|
||||||
rid: message.subscription.id,
|
rid: message.subscription ? message.subscription.id : '',
|
||||||
u: message.u,
|
u: message.u,
|
||||||
msg: message.msg
|
msg: message.msg
|
||||||
};
|
};
|
||||||
|
@ -320,7 +321,7 @@ const MessageActions = React.memo(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOptions = (message: any) => {
|
const getOptions = (message: TMessageModel) => {
|
||||||
let options: any = [];
|
let options: any = [];
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
|
@ -446,7 +447,7 @@ const MessageActions = React.memo(
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
const showMessageActions = async (message: any) => {
|
const showMessageActions = async (message: TMessageModel) => {
|
||||||
logEvent(events.ROOM_SHOW_MSG_ACTIONS);
|
logEvent(events.ROOM_SHOW_MSG_ACTIONS);
|
||||||
await getPermissions();
|
await getPermissions();
|
||||||
showActionSheet({
|
showActionSheet({
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { KeyboardRegistry } from 'react-native-ui-lib/keyboard';
|
import { KeyboardRegistry } from 'react-native-ui-lib/keyboard';
|
||||||
|
|
||||||
import store from '../../lib/createStore';
|
import { store } from '../../lib/auxStore';
|
||||||
import EmojiPicker from '../EmojiPicker';
|
import EmojiPicker from '../EmojiPicker';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
|
|
@ -6,7 +6,7 @@ import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||||
import { IEmoji } from '../../EmojiPicker/interfaces';
|
import { IEmoji } from '../../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageBoxMentionEmoji {
|
interface IMessageBoxMentionEmoji {
|
||||||
item: IEmoji;
|
item: IEmoji;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import FixedMentionItem from './FixedMentionItem';
|
||||||
import MentionEmoji from './MentionEmoji';
|
import MentionEmoji from './MentionEmoji';
|
||||||
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { IEmoji } from '../../EmojiPicker/interfaces';
|
import { IEmoji } from '../../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageBoxMentionItem {
|
interface IMessageBoxMentionItem {
|
||||||
item: {
|
item: {
|
||||||
|
|
|
@ -3,10 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import Markdown from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -42,11 +43,7 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface IMessageBoxReplyPreview {
|
interface IMessageBoxReplyPreview {
|
||||||
replying: boolean;
|
replying: boolean;
|
||||||
message: {
|
message: IMessage;
|
||||||
ts: Date;
|
|
||||||
msg: string;
|
|
||||||
u: any;
|
|
||||||
};
|
|
||||||
Message_TimeFormat: string;
|
Message_TimeFormat: string;
|
||||||
close(): void;
|
close(): void;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
@ -57,17 +54,7 @@ interface IMessageBoxReplyPreview {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReplyPreview = React.memo(
|
const ReplyPreview = React.memo(
|
||||||
({
|
({ message, Message_TimeFormat, replying, close, theme, useRealName }: IMessageBoxReplyPreview) => {
|
||||||
message,
|
|
||||||
Message_TimeFormat,
|
|
||||||
baseUrl,
|
|
||||||
username,
|
|
||||||
replying,
|
|
||||||
getCustomEmoji,
|
|
||||||
close,
|
|
||||||
theme,
|
|
||||||
useRealName
|
|
||||||
}: IMessageBoxReplyPreview) => {
|
|
||||||
if (!replying) {
|
if (!replying) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -82,16 +69,7 @@ const ReplyPreview = React.memo(
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
</View>
|
</View>
|
||||||
{/* @ts-ignore*/}
|
<MarkdownPreview msg={message.msg} />
|
||||||
<Markdown
|
|
||||||
msg={message.msg}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
username={username}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
numberOfLines={1}
|
|
||||||
preview
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
|
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { Image } from 'react-native-image-crop-picker';
|
||||||
|
|
||||||
|
import { forceJpgExtension } from './forceJpgExtension';
|
||||||
|
|
||||||
|
const attachment: Image = {
|
||||||
|
exif: null,
|
||||||
|
filename: 'IMG_0040.PNG',
|
||||||
|
path: 'tmp/temp',
|
||||||
|
height: 534,
|
||||||
|
width: 223,
|
||||||
|
data: null,
|
||||||
|
modificationDate: '1643984790',
|
||||||
|
localIdentifier: 'device/L0/001',
|
||||||
|
size: 16623,
|
||||||
|
sourceURL: '',
|
||||||
|
mime: 'image/jpeg',
|
||||||
|
cropRect: null,
|
||||||
|
creationDate: '1641490665'
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('forceJpgExtension for iOS', () => {
|
||||||
|
jest.mock('react-native', () => ({ Platform: { OS: 'ios' } }));
|
||||||
|
describe('with mime as image/jpeg', () => {
|
||||||
|
test('filename.jpg should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.jpg';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.jpg');
|
||||||
|
});
|
||||||
|
test('filename.png should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.png';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.jpg');
|
||||||
|
});
|
||||||
|
test('filename.jpeg should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.jpeg';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.jpg');
|
||||||
|
});
|
||||||
|
test('filename.heic should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.heic';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.jpg');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with mime different', () => {
|
||||||
|
test('filename.jpg should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.png';
|
||||||
|
newAttachment.mime = 'image/png';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.png');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('forceJpgExtension for android', () => {
|
||||||
|
jest.mock('react-native', () => ({ Platform: { OS: 'android' } }));
|
||||||
|
describe('with mime as image/jpeg', () => {
|
||||||
|
test('filename.jpg should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.jpg';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.jpg');
|
||||||
|
});
|
||||||
|
test('filename.png should be filename.png', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.png';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.png');
|
||||||
|
});
|
||||||
|
test('filename.jpeg should be filename.jpeg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.jpeg';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.jpeg');
|
||||||
|
});
|
||||||
|
test('filename.heic should be filename.heic', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.heic';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.heic');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with mime different', () => {
|
||||||
|
test('filename.jpg should be filename.jpg', () => {
|
||||||
|
const newAttachment = attachment;
|
||||||
|
newAttachment.filename = 'filename.png';
|
||||||
|
newAttachment.mime = 'image/png';
|
||||||
|
const file = forceJpgExtension(newAttachment);
|
||||||
|
|
||||||
|
expect(file.filename).toBe('filename.png');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { ImageOrVideo } from 'react-native-image-crop-picker';
|
||||||
|
|
||||||
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
|
|
||||||
|
const regex = new RegExp(/\.[^/.]+$/); // Check from last '.' of the string
|
||||||
|
|
||||||
|
export const forceJpgExtension = (attachment: ImageOrVideo): ImageOrVideo => {
|
||||||
|
if (isIOS && attachment.mime === 'image/jpeg' && attachment.filename) {
|
||||||
|
// Replace files extension that mime type is 'image/jpeg' to .jpg;
|
||||||
|
attachment.filename = attachment.filename.replace(regex, '.jpg');
|
||||||
|
}
|
||||||
|
return attachment;
|
||||||
|
};
|
|
@ -27,7 +27,7 @@ import LeftButtons from './LeftButtons';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// eslint-disable-next-line import/extensions,import/no-unresolved
|
// eslint-disable-next-line import/extensions,import/no-unresolved
|
||||||
import RightButtons from './RightButtons';
|
import RightButtons from './RightButtons';
|
||||||
import { isAndroid, isIOS, isTablet } from '../../utils/deviceInfo';
|
import { isAndroid, isTablet } from '../../utils/deviceInfo';
|
||||||
import { canUploadFile } from '../../utils/media';
|
import { canUploadFile } from '../../utils/media';
|
||||||
import EventEmiter from '../../utils/events';
|
import EventEmiter from '../../utils/events';
|
||||||
import { KEY_COMMAND, handleCommandShowUpload, handleCommandSubmit, handleCommandTyping } from '../../commands';
|
import { KEY_COMMAND, handleCommandShowUpload, handleCommandSubmit, handleCommandTyping } from '../../commands';
|
||||||
|
@ -47,6 +47,8 @@ import Navigation from '../../lib/Navigation';
|
||||||
import { withActionSheet } from '../ActionSheet';
|
import { withActionSheet } from '../ActionSheet';
|
||||||
import { sanitizeLikeString } from '../../lib/database/utils';
|
import { sanitizeLikeString } from '../../lib/database/utils';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
|
import { forceJpgExtension } from './forceJpgExtension';
|
||||||
|
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
require('./EmojiKeyboard');
|
require('./EmojiKeyboard');
|
||||||
|
@ -73,18 +75,14 @@ const videoPickerConfig = {
|
||||||
interface IMessageBoxProps {
|
interface IMessageBoxProps {
|
||||||
rid: string;
|
rid: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
message: {
|
message: IMessage;
|
||||||
u: {
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
id: any;
|
|
||||||
};
|
|
||||||
replying: boolean;
|
replying: boolean;
|
||||||
editing: boolean;
|
editing: boolean;
|
||||||
threadsEnabled: boolean;
|
threadsEnabled: boolean;
|
||||||
isFocused(): boolean;
|
isFocused(): boolean;
|
||||||
user: {
|
user: {
|
||||||
id: string;
|
id: string;
|
||||||
|
_id: string;
|
||||||
username: string;
|
username: string;
|
||||||
token: string;
|
token: string;
|
||||||
};
|
};
|
||||||
|
@ -130,18 +128,6 @@ interface IMessageBoxState {
|
||||||
permissionToUpload: boolean;
|
permissionToUpload: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const forceJpgExtension = (attachment: ImageOrVideo) => {
|
|
||||||
if (isIOS && attachment.mime === 'image/jpeg' && attachment.filename) {
|
|
||||||
const regex = new RegExp(/.heic$/i);
|
|
||||||
if (attachment.filename.match(regex)) {
|
|
||||||
attachment.filename = attachment.filename.replace(regex, '.jpg');
|
|
||||||
} else {
|
|
||||||
attachment.filename += '.jpg';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attachment;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
private text: string;
|
private text: string;
|
||||||
|
|
||||||
|
@ -1083,7 +1069,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
const replyPreview = !recording ? (
|
const replyPreview = !recording ? (
|
||||||
<ReplyPreview
|
<ReplyPreview
|
||||||
// @ts-ignore
|
|
||||||
message={message}
|
message={message}
|
||||||
close={replyCancel}
|
close={replyCancel}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
|
@ -1200,5 +1185,5 @@ const mapStateToProps = (state: any) => ({
|
||||||
const dispatchToProps = {
|
const dispatchToProps = {
|
||||||
typing: (rid: any, status: any) => userTypingAction(rid, status)
|
typing: (rid: any, status: any) => userTypingAction(rid, status)
|
||||||
};
|
};
|
||||||
// @ts-ignore
|
|
||||||
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)) as any;
|
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)) as any;
|
||||||
|
|
|
@ -36,7 +36,7 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
|
||||||
try {
|
try {
|
||||||
// Find the thread header and update it
|
// Find the thread header and update it
|
||||||
const msg = await msgCollection.find(tmid);
|
const msg = await msgCollection.find(tmid);
|
||||||
if (msg.tcount <= 1) {
|
if (msg?.tcount && msg.tcount <= 1) {
|
||||||
deleteBatch.push(
|
deleteBatch.push(
|
||||||
msg.prepareUpdate((m: any) => {
|
msg.prepareUpdate((m: any) => {
|
||||||
m.tcount = null;
|
m.tcount = null;
|
||||||
|
@ -62,7 +62,7 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
|
||||||
// Do nothing: message not found
|
// Do nothing: message not found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await db.action(async () => {
|
await db.write(async () => {
|
||||||
await db.batch(...deleteBatch);
|
await db.batch(...deleteBatch);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { CustomIcon } from '../lib/Icons';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
|
import { TGetCustomEmoji } from '../definitions/IEmoji';
|
||||||
import SafeAreaView from './SafeAreaView';
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -66,7 +67,7 @@ interface IItem {
|
||||||
};
|
};
|
||||||
user?: { username: any };
|
user?: { username: any };
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import Markdown from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import RoomTypeIcon from '../RoomTypeIcon';
|
import RoomTypeIcon from '../RoomTypeIcon';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
|
||||||
|
@ -101,16 +101,7 @@ const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, theme, scale }
|
||||||
|
|
||||||
// subtitle
|
// subtitle
|
||||||
if (subtitle) {
|
if (subtitle) {
|
||||||
return (
|
return <MarkdownPreview msg={subtitle} style={[styles.subtitle, { fontSize, color: themes[theme].auxiliaryText }]} />;
|
||||||
// @ts-ignore
|
|
||||||
<Markdown
|
|
||||||
preview
|
|
||||||
msg={subtitle}
|
|
||||||
style={[styles.subtitle, { fontSize, color: themes[theme].auxiliaryText }]}
|
|
||||||
numberOfLines={1}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -126,10 +117,7 @@ const HeaderTitle = React.memo(({ title, tmid, prid, scale, theme, testID }: TRo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <MarkdownPreview msg={title} style={[styles.title, titleStyle]} testID={testID} />;
|
||||||
// @ts-ignore
|
|
||||||
<Markdown preview msg={title} style={[styles.title, titleStyle]} numberOfLines={1} theme={theme} testID={testID} />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Header = React.memo(
|
const Header = React.memo(
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,11 @@
|
||||||
|
import { dequal } from 'dequal';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
|
||||||
|
|
||||||
import RoomHeader from './RoomHeader';
|
import { IApplicationState } from '../../definitions';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
import RoomHeader from './RoomHeader';
|
||||||
|
|
||||||
interface IRoomHeaderContainerProps {
|
interface IRoomHeaderContainerProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -122,8 +123,8 @@ class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any, ownProps: any) => {
|
const mapStateToProps = (state: IApplicationState, ownProps: any) => {
|
||||||
let statusText;
|
let statusText = '';
|
||||||
let status = 'offline';
|
let status = 'offline';
|
||||||
const { roomUserId, type, visitor = {}, tmid } = ownProps;
|
const { roomUserId, type, visitor = {}, tmid } = ownProps;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NativeSyntheticEvent, StyleSheet, Text, TextInputFocusEventData, TextInputProps, View } from 'react-native';
|
import {
|
||||||
|
NativeSyntheticEvent,
|
||||||
|
StyleSheet,
|
||||||
|
TextInput as RNTextInput,
|
||||||
|
Text,
|
||||||
|
TextInputFocusEventData,
|
||||||
|
TextInputProps,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../presentation/TextInput';
|
||||||
|
@ -51,7 +59,7 @@ interface ISearchBox {
|
||||||
hasCancel?: boolean;
|
hasCancel?: boolean;
|
||||||
onCancelPress?: Function;
|
onCancelPress?: Function;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
inputRef?: React.Ref<unknown>;
|
inputRef?: React.Ref<RNTextInput>;
|
||||||
testID?: string;
|
testID?: string;
|
||||||
onFocus?: (e: NativeSyntheticEvent<TextInputFocusEventData>) => void;
|
onFocus?: (e: NativeSyntheticEvent<TextInputFocusEventData>) => void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { withTheme } from '../theme';
|
import I18n from '../i18n';
|
||||||
|
import { useTheme } from '../theme';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../presentation/TextInput';
|
||||||
|
@ -19,14 +20,13 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ISearchHeader {
|
interface ISearchHeaderProps {
|
||||||
theme?: string;
|
|
||||||
onSearchChangeText?: (text: string) => void;
|
onSearchChangeText?: (text: string) => void;
|
||||||
|
testID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: it might be useful to refactor this component for reusage
|
const SearchHeader = ({ onSearchChangeText, testID }: ISearchHeaderProps): JSX.Element => {
|
||||||
const SearchHeader = ({ theme, onSearchChangeText }: ISearchHeader) => {
|
const { theme } = useTheme();
|
||||||
const titleColorStyle = { color: themes[theme!].headerTitleColor };
|
|
||||||
const isLight = theme === 'light';
|
const isLight = theme === 'light';
|
||||||
const { isLandscape } = useOrientation();
|
const { isLandscape } = useOrientation();
|
||||||
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
||||||
|
@ -36,14 +36,14 @@ const SearchHeader = ({ theme, onSearchChangeText }: ISearchHeader) => {
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoFocus
|
autoFocus
|
||||||
style={[styles.title, isLight && titleColorStyle, { fontSize: titleFontSize }]}
|
style={[styles.title, isLight && { color: themes[theme].headerTitleColor }, { fontSize: titleFontSize }]}
|
||||||
placeholder='Search'
|
placeholder={I18n.t('Search')}
|
||||||
onChangeText={onSearchChangeText}
|
onChangeText={onSearchChangeText}
|
||||||
theme={theme!}
|
theme={theme}
|
||||||
testID='thread-messages-view-search-header'
|
testID={testID}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTheme(SearchHeader);
|
export default SearchHeader;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleProp, StyleSheet, Text, TextInputProps, TextStyle, View, ViewStyle } from 'react-native';
|
import { StyleProp, StyleSheet, Text, TextInputProps, TextInput as RNTextInput, TextStyle, View, ViewStyle } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -59,7 +59,7 @@ export interface IRCTextInputProps extends TextInputProps {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
containerStyle?: StyleProp<ViewStyle>;
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
inputStyle?: StyleProp<TextStyle>;
|
inputStyle?: StyleProp<TextStyle>;
|
||||||
inputRef?: React.Ref<unknown>;
|
inputRef?: React.Ref<RNTextInput>;
|
||||||
testID?: string;
|
testID?: string;
|
||||||
iconLeft?: string;
|
iconLeft?: string;
|
||||||
iconRight?: string;
|
iconRight?: string;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Touchable from 'react-native-platform-touchable';
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { withTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { TThreadModel } from '../definitions/IThread';
|
import { TThreadModel } from '../definitions/IThread';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -48,12 +48,12 @@ interface IThreadDetails {
|
||||||
badgeColor?: string;
|
badgeColor?: string;
|
||||||
toggleFollowThread: Function;
|
toggleFollowThread: Function;
|
||||||
style: ViewStyle;
|
style: ViewStyle;
|
||||||
theme?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style, theme }: IThreadDetails) => {
|
const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IThreadDetails): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
let { tcount } = item;
|
let { tcount } = item;
|
||||||
if (tcount! >= 1000) {
|
if (tcount && tcount >= 1000) {
|
||||||
tcount = '+999';
|
tcount = '+999';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,6 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style, them
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.badgeContainer}>
|
<View style={styles.badgeContainer}>
|
||||||
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null}
|
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null}
|
||||||
<Touchable onPress={() => toggleFollowThread?.(isFollowing, item.id)}>
|
<Touchable onPress={() => toggleFollowThread?.(isFollowing, item.id)}>
|
||||||
|
@ -96,4 +95,4 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style, them
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTheme(ThreadDetails);
|
export default ThreadDetails;
|
||||||
|
|
|
@ -22,8 +22,8 @@ interface IMultiSelect {
|
||||||
context?: number;
|
context?: number;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
multiselect?: boolean;
|
multiselect?: boolean;
|
||||||
onSearch: Function;
|
onSearch?: () => void;
|
||||||
onClose: Function;
|
onClose?: () => void;
|
||||||
inputStyle: object;
|
inputStyle: object;
|
||||||
value?: any[];
|
value?: any[];
|
||||||
disabled?: boolean | object;
|
disabled?: boolean | object;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, { useContext } from 'react';
|
||||||
import { StyleSheet, Text } from 'react-native';
|
import { StyleSheet, Text } from 'react-native';
|
||||||
import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKitModal } from '@rocket.chat/ui-kit';
|
import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKitModal } from '@rocket.chat/ui-kit';
|
||||||
|
|
||||||
import Markdown from '../markdown';
|
import Markdown, { MarkdownPreview } from '../markdown';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import TextInput from '../TextInput';
|
import TextInput from '../TextInput';
|
||||||
import { textParser, useBlockContext } from './utils';
|
import { textParser, useBlockContext } from './utils';
|
||||||
|
@ -49,10 +49,10 @@ class MessageParser extends UiKitParserMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isContext = context === BLOCK_CONTEXT.CONTEXT;
|
const isContext = context === BLOCK_CONTEXT.CONTEXT;
|
||||||
return (
|
if (isContext) {
|
||||||
// @ts-ignore
|
return <MarkdownPreview msg={text} style={[isContext && { color: themes[theme].auxiliaryText }]} numberOfLines={0} />;
|
||||||
<Markdown msg={text} theme={theme} style={[isContext && { color: themes[theme].auxiliaryText }]} preview={isContext} />
|
}
|
||||||
);
|
return <Markdown msg={text} theme={theme} style={[isContext && { color: themes[theme].auxiliaryText }]} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
button(element: any, context: any) {
|
button(element: any, context: any) {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Storyshots Text Input Short and Long Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"paddingHorizontal\\":14}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10,\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"},null]},\\"children\\":[\\"Short Text\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"allowFontScaling\\":true,\\"rejectResponderTermination\\":true,\\"underlineColorAndroid\\":\\"transparent\\",\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,null,{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"accessibilityLabel\\":\\"placeholder\\",\\"placeholder\\":\\"placeholder\\",\\"value\\":\\"Rocket.Chat\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10,\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"},null]},\\"children\\":[\\"Long Text\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"allowFontScaling\\":true,\\"rejectResponderTermination\\":true,\\"underlineColorAndroid\\":\\"transparent\\",\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,null,{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"accessibilityLabel\\":\\"placeholder\\",\\"placeholder\\":\\"placeholder\\",\\"value\\":\\"https://open.rocket.chat/images/logo/android-chrome-512x512.png\\"},\\"children\\":null}]}]}]}"`;
|
|
@ -8,10 +8,10 @@ import { events, logEvent } from '../../utils/log';
|
||||||
|
|
||||||
interface IAtMention {
|
interface IAtMention {
|
||||||
mention: string;
|
mention: string;
|
||||||
username: string;
|
username?: string;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo?: Function;
|
||||||
style?: any;
|
style?: any;
|
||||||
useRealName: boolean;
|
useRealName?: boolean;
|
||||||
mentions: any;
|
mentions: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
||||||
t: 'd',
|
t: 'd',
|
||||||
rid: user && user._id
|
rid: user && user._id
|
||||||
};
|
};
|
||||||
navToRoomInfo(navParam);
|
if (navToRoomInfo) {
|
||||||
|
navToRoomInfo(navParam);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ interface IEmoji {
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: Function;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
customEmojis?: any;
|
customEmojis?: any;
|
||||||
style: object;
|
style?: object;
|
||||||
theme?: string;
|
theme: string;
|
||||||
onEmojiSelected?: Function;
|
onEmojiSelected?: Function;
|
||||||
tabEmojiStyle?: object;
|
tabEmojiStyle?: object;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ const Emoji = React.memo(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text style={[{ color: themes[theme!].bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
<Text style={[{ color: themes[theme].bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
||||||
{emojiUnicode}
|
{emojiUnicode}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, TextStyle } from 'react-native';
|
import { Text, TextStyle, StyleProp } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
import { IUserChannel } from './interfaces';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IHashtag {
|
interface IHashtag {
|
||||||
hashtag: string;
|
hashtag: string;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo?: Function;
|
||||||
style?: TextStyle[];
|
style?: StyleProp<TextStyle>[];
|
||||||
channels: {
|
channels?: IUserChannel[];
|
||||||
[index: number]: string | number;
|
|
||||||
name: string;
|
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
||||||
|
@ -21,11 +18,13 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
const index = channels?.findIndex(channel => channel.name === hashtag);
|
const index = channels?.findIndex(channel => channel.name === hashtag);
|
||||||
const navParam = {
|
if (index && navToRoomInfo) {
|
||||||
t: 'c',
|
const navParam = {
|
||||||
rid: channels[index]._id
|
t: 'c',
|
||||||
};
|
rid: channels?.[index]._id
|
||||||
navToRoomInfo(navParam);
|
};
|
||||||
|
navToRoomInfo(navParam);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) {
|
if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) {
|
||||||
|
|
|
@ -7,12 +7,13 @@ import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
|
import { TOnLinkPress } from './interfaces';
|
||||||
|
|
||||||
interface ILink {
|
interface ILink {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
link: string;
|
link: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
onLinkPress: Function;
|
onLinkPress?: TOnLinkPress;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ interface IList {
|
||||||
ordered: boolean;
|
ordered: boolean;
|
||||||
start: number;
|
start: number;
|
||||||
tight: boolean;
|
tight: boolean;
|
||||||
numberOfLines: number;
|
numberOfLines?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const List = React.memo(({ children, ordered, tight, start = 1, numberOfLines = 0 }: IList) => {
|
const List = React.memo(({ children, ordered, tight, start = 1, numberOfLines = 0 }: IList) => {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||||
|
import removeMarkdown from 'remove-markdown';
|
||||||
|
|
||||||
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
|
import { themes } from '../../constants/colors';
|
||||||
|
import { formatText } from './formatText';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import styles from './styles';
|
||||||
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
|
|
||||||
|
interface IMarkdownPreview {
|
||||||
|
msg?: string;
|
||||||
|
numberOfLines?: number;
|
||||||
|
testID?: string;
|
||||||
|
style?: StyleProp<TextStyle>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarkdownPreview = ({ msg, numberOfLines = 1, testID, style = [] }: IMarkdownPreview): React.ReactElement | null => {
|
||||||
|
if (!msg) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
let m = formatText(msg);
|
||||||
|
m = formatHyperlink(m);
|
||||||
|
m = shortnameToUnicode(m);
|
||||||
|
// Removes sequential empty spaces
|
||||||
|
m = m.replace(/\s+/g, ' ');
|
||||||
|
m = removeMarkdown(m);
|
||||||
|
m = m.replace(/\n+/g, ' ');
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
accessibilityLabel={m}
|
||||||
|
style={[styles.text, { color: themes[theme].bodyText }, ...style]}
|
||||||
|
numberOfLines={numberOfLines}
|
||||||
|
testID={testID}>
|
||||||
|
{m}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MarkdownPreview;
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
|
|
||||||
|
describe('FormatText', () => {
|
||||||
|
test('empty to be empty', () => {
|
||||||
|
expect(formatHyperlink('')).toBe('');
|
||||||
|
});
|
||||||
|
test('A123 to be A123', () => {
|
||||||
|
expect(formatHyperlink('A123')).toBe('A123');
|
||||||
|
});
|
||||||
|
test('Format <http://link|Text> to be <http://link|Text>', () => {
|
||||||
|
expect(formatHyperlink('<http://link|Text>')).toBe('<http://link|Text>');
|
||||||
|
});
|
||||||
|
test('Format "[ ](https://open.rocket.chat/) Test" to be Test', () => {
|
||||||
|
expect(formatHyperlink('[ ](https://open.rocket.chat/) Test')).toBe('Test');
|
||||||
|
});
|
||||||
|
test('Format "[Open](https://open.rocket.chat/) Test" to be Test', () => {
|
||||||
|
expect(formatHyperlink('[Open](https://open.rocket.chat/) Test')).toBe('[Open](https://open.rocket.chat/) Test');
|
||||||
|
});
|
||||||
|
test('render test (arabic)', () => {
|
||||||
|
expect(formatHyperlink('[ ](https://open.rocket.chat/) اختبا')).toBe('اختبا');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('render test (russian)', () => {
|
||||||
|
expect(formatHyperlink('[ ](https://open.rocket.chat/) тест123')).toBe('тест123');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
||||||
|
// Return: 'Test'
|
||||||
|
export const formatHyperlink = (text: string): string => text.replace(/^\[([\s]*)\]\(([^)]*)\)\s/, '').trim();
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { formatText } from './formatText';
|
||||||
|
|
||||||
|
describe('FormatText', () => {
|
||||||
|
test('empty to be empty', () => {
|
||||||
|
expect(formatText('')).toBe('');
|
||||||
|
});
|
||||||
|
test('A123 to be A123', () => {
|
||||||
|
expect(formatText('A123')).toBe('A123');
|
||||||
|
});
|
||||||
|
test('Format <http://link|Text> to be [Text](http://link)', () => {
|
||||||
|
expect(formatText('<http://link|Text>')).toBe('[Text](http://link)');
|
||||||
|
});
|
||||||
|
test('render test (arabic)', () => {
|
||||||
|
expect(formatText('اختبا <http://link|ر123>')).toBe('اختبا [ر123](http://link)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('render test (russian)', () => {
|
||||||
|
expect(formatText('<http://link|тест123>')).toBe('[тест123](http://link)');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Support <http://link|Text>
|
||||||
|
export const formatText = (text: string): string =>
|
||||||
|
text.replace(
|
||||||
|
new RegExp('(?:<|<)((?:https|http):\\/\\/[^\\|]+)\\|(.+?)(?=>|>)(?:>|>)', 'gm'),
|
||||||
|
(match, url, title) => `[${title}](${url})`
|
||||||
|
);
|
|
@ -1,12 +1,9 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Image, Text } from 'react-native';
|
import { Image, StyleProp, Text, TextStyle } from 'react-native';
|
||||||
import { Node, Parser } from 'commonmark';
|
import { Node, Parser } from 'commonmark';
|
||||||
import Renderer from 'commonmark-react-renderer';
|
import Renderer from 'commonmark-react-renderer';
|
||||||
import removeMarkdown from 'remove-markdown';
|
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import { UserMention } from '../message/interfaces';
|
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import MarkdownLink from './Link';
|
import MarkdownLink from './Link';
|
||||||
|
@ -23,43 +20,39 @@ import mergeTextNodes from './mergeTextNodes';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { isValidURL } from '../../utils/url';
|
import { isValidURL } from '../../utils/url';
|
||||||
import NewMarkdown from './new';
|
import NewMarkdown from './new';
|
||||||
|
import { formatText } from './formatText';
|
||||||
|
import { IUserMention, IUserChannel, TOnLinkPress } from './interfaces';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
|
|
||||||
|
export { default as MarkdownPreview } from './Preview';
|
||||||
|
|
||||||
interface IMarkdownProps {
|
interface IMarkdownProps {
|
||||||
msg: string;
|
msg?: string;
|
||||||
md: MarkdownAST;
|
|
||||||
mentions: UserMention[];
|
|
||||||
getCustomEmoji: Function;
|
|
||||||
baseUrl: string;
|
|
||||||
username: string;
|
|
||||||
tmid: string;
|
|
||||||
isEdited: boolean;
|
|
||||||
numberOfLines: number;
|
|
||||||
customEmojis: boolean;
|
|
||||||
useRealName: boolean;
|
|
||||||
channels: {
|
|
||||||
name: string;
|
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
enableMessageParser: boolean;
|
|
||||||
navToRoomInfo: Function;
|
|
||||||
preview: boolean;
|
|
||||||
theme: string;
|
theme: string;
|
||||||
testID: string;
|
md?: MarkdownAST;
|
||||||
style: any;
|
mentions?: IUserMention[];
|
||||||
onLinkPress: Function;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
|
baseUrl?: string;
|
||||||
|
username?: string;
|
||||||
|
tmid?: string;
|
||||||
|
isEdited?: boolean;
|
||||||
|
numberOfLines?: number;
|
||||||
|
customEmojis?: boolean;
|
||||||
|
useRealName?: boolean;
|
||||||
|
channels?: IUserChannel[];
|
||||||
|
enableMessageParser?: boolean;
|
||||||
|
// TODO: Refactor when migrate Room
|
||||||
|
navToRoomInfo?: Function;
|
||||||
|
testID?: string;
|
||||||
|
style?: StyleProp<TextStyle>[];
|
||||||
|
onLinkPress?: TOnLinkPress;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TLiteral = {
|
type TLiteral = {
|
||||||
literal: string;
|
literal: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Support <http://link|Text>
|
|
||||||
const formatText = (text: string) =>
|
|
||||||
text.replace(
|
|
||||||
new RegExp('(?:<|<)((?:https|http):\\/\\/[^\\|]+)\\|(.+?)(?=>|>)(?:>|>)', 'gm'),
|
|
||||||
(match, url, title) => `[${title}](${url})`
|
|
||||||
);
|
|
||||||
|
|
||||||
const emojiRanges = [
|
const emojiRanges = [
|
||||||
'\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]', // unicode emoji from https://www.regextester.com/106421
|
'\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]', // unicode emoji from https://www.regextester.com/106421
|
||||||
':.{1,40}:', // custom emoji
|
':.{1,40}:', // custom emoji
|
||||||
|
@ -148,7 +141,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
|
|
||||||
get isNewMarkdown(): boolean {
|
get isNewMarkdown(): boolean {
|
||||||
const { md, enableMessageParser } = this.props;
|
const { md, enableMessageParser } = this.props;
|
||||||
return enableMessageParser && !!md;
|
return !!enableMessageParser && !!md;
|
||||||
}
|
}
|
||||||
|
|
||||||
editedMessage = (ast: any) => {
|
editedMessage = (ast: any) => {
|
||||||
|
@ -244,7 +237,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
||||||
const { username, mentions, navToRoomInfo, useRealName, style } = this.props;
|
const { username = '', mentions, navToRoomInfo, useRealName, style } = this.props;
|
||||||
return (
|
return (
|
||||||
<MarkdownAtMention
|
<MarkdownAtMention
|
||||||
mentions={mentions}
|
mentions={mentions}
|
||||||
|
@ -258,7 +251,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderEmoji = ({ literal }: TLiteral) => {
|
renderEmoji = ({ literal }: TLiteral) => {
|
||||||
const { getCustomEmoji, baseUrl, customEmojis, style, theme } = this.props;
|
const { getCustomEmoji, baseUrl = '', customEmojis, style, theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<MarkdownEmoji
|
<MarkdownEmoji
|
||||||
literal={literal}
|
literal={literal}
|
||||||
|
@ -343,18 +336,13 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
const {
|
const {
|
||||||
msg,
|
msg,
|
||||||
md,
|
md,
|
||||||
numberOfLines,
|
|
||||||
preview = false,
|
|
||||||
theme,
|
|
||||||
style = [],
|
|
||||||
testID,
|
|
||||||
mentions,
|
mentions,
|
||||||
channels,
|
channels,
|
||||||
navToRoomInfo,
|
navToRoomInfo,
|
||||||
useRealName,
|
useRealName,
|
||||||
username,
|
username = '',
|
||||||
getCustomEmoji,
|
getCustomEmoji,
|
||||||
baseUrl,
|
baseUrl = '',
|
||||||
onLinkPress
|
onLinkPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -362,7 +350,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isNewMarkdown && !preview) {
|
if (this.isNewMarkdown) {
|
||||||
return (
|
return (
|
||||||
<NewMarkdown
|
<NewMarkdown
|
||||||
username={username}
|
username={username}
|
||||||
|
@ -379,28 +367,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = formatText(msg);
|
let m = formatText(msg);
|
||||||
|
m = formatHyperlink(m);
|
||||||
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
|
||||||
// Return: 'Test'
|
|
||||||
m = m.replace(/^\[([\s]*)\]\(([^)]*)\)\s/, '').trim();
|
|
||||||
|
|
||||||
if (preview) {
|
|
||||||
m = shortnameToUnicode(m);
|
|
||||||
// Removes sequential empty spaces
|
|
||||||
m = m.replace(/\s+/g, ' ');
|
|
||||||
m = removeMarkdown(m);
|
|
||||||
m = m.replace(/\n+/g, ' ');
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
accessibilityLabel={m}
|
|
||||||
style={[styles.text, { color: themes[theme].bodyText }, ...style]}
|
|
||||||
numberOfLines={numberOfLines}
|
|
||||||
testID={testID}>
|
|
||||||
{m}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ast = parser.parse(m);
|
let ast = parser.parse(m);
|
||||||
ast = mergeTextNodes(ast);
|
ast = mergeTextNodes(ast);
|
||||||
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface IUserMention {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserChannel {
|
||||||
|
name: string;
|
||||||
|
_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TOnLinkPress = (link: string) => void;
|
|
@ -1,17 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { UserMention } from '../../message/interfaces';
|
import { IUserMention, IUserChannel } from '../interfaces';
|
||||||
|
|
||||||
interface IMarkdownContext {
|
interface IMarkdownContext {
|
||||||
mentions: UserMention[];
|
mentions?: IUserMention[];
|
||||||
channels: {
|
channels?: IUserChannel[];
|
||||||
name: string;
|
useRealName?: boolean;
|
||||||
_id: number;
|
username?: string;
|
||||||
}[];
|
baseUrl?: string;
|
||||||
useRealName: boolean;
|
navToRoomInfo?: Function;
|
||||||
username: string;
|
|
||||||
baseUrl: string;
|
|
||||||
navToRoomInfo: Function;
|
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: Function;
|
||||||
onLinkPress?: Function;
|
onLinkPress?: Function;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
import Quote from './Quote';
|
import Quote from './Quote';
|
||||||
import Paragraph from './Paragraph';
|
import Paragraph from './Paragraph';
|
||||||
|
@ -8,21 +9,18 @@ import Code from './Code';
|
||||||
import BigEmoji from './BigEmoji';
|
import BigEmoji from './BigEmoji';
|
||||||
import OrderedList from './OrderedList';
|
import OrderedList from './OrderedList';
|
||||||
import UnorderedList from './UnorderedList';
|
import UnorderedList from './UnorderedList';
|
||||||
import { UserMention } from '../../message/interfaces';
|
import { IUserMention, IUserChannel, TOnLinkPress } from '../interfaces';
|
||||||
import TaskList from './TaskList';
|
import TaskList from './TaskList';
|
||||||
import MarkdownContext from './MarkdownContext';
|
import MarkdownContext from './MarkdownContext';
|
||||||
|
|
||||||
interface IBodyProps {
|
interface IBodyProps {
|
||||||
tokens: MarkdownAST;
|
tokens?: MarkdownAST;
|
||||||
mentions: UserMention[];
|
mentions?: IUserMention[];
|
||||||
channels: {
|
channels?: IUserChannel[];
|
||||||
name: string;
|
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: Function;
|
||||||
onLinkPress?: Function;
|
onLinkPress?: TOnLinkPress;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo?: Function;
|
||||||
useRealName: boolean;
|
useRealName?: boolean;
|
||||||
username: string;
|
username: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
}
|
}
|
||||||
|
@ -37,41 +35,47 @@ const Body = ({
|
||||||
getCustomEmoji,
|
getCustomEmoji,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onLinkPress
|
onLinkPress
|
||||||
}: IBodyProps): JSX.Element => (
|
}: IBodyProps): React.ReactElement | null => {
|
||||||
<MarkdownContext.Provider
|
if (isEmpty(tokens)) {
|
||||||
value={{
|
return null;
|
||||||
mentions,
|
}
|
||||||
channels,
|
|
||||||
useRealName,
|
return (
|
||||||
username,
|
<MarkdownContext.Provider
|
||||||
navToRoomInfo,
|
value={{
|
||||||
getCustomEmoji,
|
mentions,
|
||||||
baseUrl,
|
channels,
|
||||||
onLinkPress
|
useRealName,
|
||||||
}}>
|
username,
|
||||||
{tokens.map(block => {
|
navToRoomInfo,
|
||||||
switch (block.type) {
|
getCustomEmoji,
|
||||||
case 'BIG_EMOJI':
|
baseUrl,
|
||||||
return <BigEmoji value={block.value} />;
|
onLinkPress
|
||||||
case 'UNORDERED_LIST':
|
}}>
|
||||||
return <UnorderedList value={block.value} />;
|
{tokens?.map(block => {
|
||||||
case 'ORDERED_LIST':
|
switch (block.type) {
|
||||||
return <OrderedList value={block.value} />;
|
case 'BIG_EMOJI':
|
||||||
case 'TASKS':
|
return <BigEmoji value={block.value} />;
|
||||||
return <TaskList value={block.value} />;
|
case 'UNORDERED_LIST':
|
||||||
case 'QUOTE':
|
return <UnorderedList value={block.value} />;
|
||||||
return <Quote value={block.value} />;
|
case 'ORDERED_LIST':
|
||||||
case 'PARAGRAPH':
|
return <OrderedList value={block.value} />;
|
||||||
return <Paragraph value={block.value} />;
|
case 'TASKS':
|
||||||
case 'CODE':
|
return <TaskList value={block.value} />;
|
||||||
return <Code value={block.value} />;
|
case 'QUOTE':
|
||||||
case 'HEADING':
|
return <Quote value={block.value} />;
|
||||||
return <Heading value={block.value} level={block.level} />;
|
case 'PARAGRAPH':
|
||||||
default:
|
return <Paragraph value={block.value} />;
|
||||||
return null;
|
case 'CODE':
|
||||||
}
|
return <Code value={block.value} />;
|
||||||
})}
|
case 'HEADING':
|
||||||
</MarkdownContext.Provider>
|
return <Heading value={block.value} level={block.level} />;
|
||||||
);
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</MarkdownContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Body;
|
export default Body;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { isAndroid, isIOS } from '../../utils/deviceInfo';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IButton {
|
interface IButton {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
@ -29,7 +30,7 @@ interface IMessageAudioProps {
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
scale?: number;
|
scale?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +281,6 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
|
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
|
||||||
</View>
|
</View>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { dequal } from 'dequal';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import Markdown from '../markdown';
|
import Markdown, { MarkdownPreview } from '../markdown';
|
||||||
import User from './User';
|
import User from './User';
|
||||||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -49,10 +49,11 @@ const Content = React.memo(
|
||||||
{I18n.t('Encrypted_message')}
|
{I18n.t('Encrypted_message')}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
} else if (isPreview) {
|
||||||
|
content = <MarkdownPreview msg={props.msg} />;
|
||||||
} else {
|
} else {
|
||||||
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
|
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
|
||||||
content = (
|
content = (
|
||||||
// @ts-ignore
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={props.msg}
|
msg={props.msg}
|
||||||
md={props.md}
|
md={props.md}
|
||||||
|
@ -61,8 +62,6 @@ const Content = React.memo(
|
||||||
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
isEdited={props.isEdited}
|
isEdited={props.isEdited}
|
||||||
numberOfLines={isPreview ? 1 : 0}
|
|
||||||
preview={isPreview}
|
|
||||||
channels={props.channels}
|
channels={props.channels}
|
||||||
mentions={props.mentions}
|
mentions={props.mentions}
|
||||||
navToRoomInfo={props.navToRoomInfo}
|
navToRoomInfo={props.navToRoomInfo}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import styles from './styles';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
type TMessageButton = {
|
type TMessageButton = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
|
@ -28,7 +29,7 @@ interface IMessageImage {
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
showAttachment: Function;
|
showAttachment: Function;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageProgress = createImageProgress(FastImage);
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
@ -66,7 +67,6 @@ const ImageContainer = React.memo(
|
||||||
<Button theme={theme} onPress={onPress}>
|
<Button theme={theme} onPress={onPress}>
|
||||||
<View>
|
<View>
|
||||||
<MessageImage img={img} theme={theme} />
|
<MessageImage img={img} theme={theme} />
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={file.description}
|
msg={file.description}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
|
|
|
@ -107,7 +107,7 @@ const MessageTouchable = React.memo((props: IMessageTouchable & IMessage) => {
|
||||||
<Touchable
|
<Touchable
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp}
|
disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp || props.type === 'jitsi_call_started'}
|
||||||
style={{ backgroundColor: props.highlighted ? themes[props.theme].headerBackground : null }}>
|
style={{ backgroundColor: props.highlighted ? themes[props.theme].headerBackground : null }}>
|
||||||
<View>
|
<View>
|
||||||
<Message {...props} />
|
<Message {...props} />
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { BUTTON_HIT_SLOP } from './utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageAddReaction {
|
interface IMessageAddReaction {
|
||||||
theme: string;
|
theme: string;
|
||||||
|
@ -19,13 +20,13 @@ interface IMessageReaction {
|
||||||
usernames: [];
|
usernames: [];
|
||||||
emoji: object;
|
emoji: object;
|
||||||
};
|
};
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageReactions {
|
interface IMessageReactions {
|
||||||
reactions?: object[];
|
reactions?: object[];
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import Markdown from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import { IMessageRepliedThread } from './interfaces';
|
import { IMessageRepliedThread } from './interfaces';
|
||||||
|
|
||||||
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme }: IMessageRepliedThread) => {
|
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme }: IMessageRepliedThread) => {
|
||||||
|
@ -32,14 +32,7 @@ const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncry
|
||||||
return (
|
return (
|
||||||
<View style={styles.repliedThread} testID={`message-thread-replied-on-${msg}`}>
|
<View style={styles.repliedThread} testID={`message-thread-replied-on-${msg}`}>
|
||||||
<CustomIcon name='threads' size={20} style={styles.repliedThreadIcon} color={themes[theme].tintColor} />
|
<CustomIcon name='threads' size={20} style={styles.repliedThreadIcon} color={themes[theme].tintColor} />
|
||||||
{/* @ts-ignore*/}
|
<MarkdownPreview msg={msg} style={[styles.repliedThreadName, { color: themes[theme].tintColor }]} />
|
||||||
<Markdown
|
|
||||||
msg={msg}
|
|
||||||
theme={theme}
|
|
||||||
style={[styles.repliedThreadName, { color: themes[theme].tintColor }]}
|
|
||||||
preview
|
|
||||||
numberOfLines={1}
|
|
||||||
/>
|
|
||||||
<View style={styles.repliedThreadDisclosure}>
|
<View style={styles.repliedThreadDisclosure}>
|
||||||
<CustomIcon name='chevron-right' color={themes[theme].auxiliaryText} size={20} />
|
<CustomIcon name='chevron-right' color={themes[theme].auxiliaryText} size={20} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import MessageContext from './Context';
|
||||||
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { IAttachment } from '../../definitions/IAttachment';
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -99,14 +100,14 @@ interface IMessageTitle {
|
||||||
|
|
||||||
interface IMessageDescription {
|
interface IMessageDescription {
|
||||||
attachment: IAttachment;
|
attachment: IAttachment;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageFields {
|
interface IMessageFields {
|
||||||
attachment: IAttachment;
|
attachment: IAttachment;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageReply {
|
interface IMessageReply {
|
||||||
|
@ -114,7 +115,7 @@ interface IMessageReply {
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
index: number;
|
index: number;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
||||||
|
@ -137,10 +138,7 @@ const Description = React.memo(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { baseUrl, user } = useContext(MessageContext);
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
return (
|
return <Markdown msg={text} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||||
// @ts-ignore
|
|
||||||
<Markdown msg={text} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) => {
|
(prevProps, nextProps) => {
|
||||||
if (prevProps.attachment.text !== nextProps.attachment.text) {
|
if (prevProps.attachment.text !== nextProps.attachment.text) {
|
||||||
|
@ -180,9 +178,8 @@ const Fields = React.memo(
|
||||||
{attachment.fields.map(field => (
|
{attachment.fields.map(field => (
|
||||||
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
|
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
|
||||||
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={field.value!}
|
msg={field?.value || ''}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
@ -214,7 +211,7 @@ const Reply = React.memo(
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (attachment.type === 'file') {
|
if (attachment.type === 'file' && attachment.title_link) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
url = formatAttachmentUrl(attachment.title_link, user.id, user.token, baseUrl);
|
url = formatAttachmentUrl(attachment.title_link, user.id, user.token, baseUrl);
|
||||||
await fileDownloadAndPreview(url, attachment);
|
await fileDownloadAndPreview(url, attachment);
|
||||||
|
@ -266,9 +263,8 @@ const Reply = React.memo(
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</Touchable>
|
</Touchable>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={attachment.description!}
|
msg={attachment.description}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue