Compare commits
8 Commits
v1.12.0
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78aba1fcb6 | ||
|
|
cba4908854 | ||
|
|
9169f6f694 | ||
|
|
bdc487ab20 | ||
|
|
d7cc95c7f8 | ||
|
|
52ad889bfc | ||
|
|
7be6911b2c | ||
|
|
509dfe5542 |
@@ -11,8 +11,7 @@
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react",
|
||||
"header"
|
||||
"react"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
@@ -32,8 +31,7 @@
|
||||
"expect": true,
|
||||
"it": true,
|
||||
"jest": true,
|
||||
"test": true,
|
||||
"__DEV__": true
|
||||
"test": true
|
||||
},
|
||||
"rules": {
|
||||
"array-bracket-spacing": [2, "never"],
|
||||
@@ -48,7 +46,7 @@
|
||||
"comma-dangle": [2, "always-multiline"],
|
||||
"comma-spacing": [2, {"before": false, "after": true}],
|
||||
"comma-style": [2, "last"],
|
||||
"complexity": [0, 10],
|
||||
"complexity": [1, 10],
|
||||
"computed-property-spacing": [2, "never"],
|
||||
"consistent-return": 2,
|
||||
"consistent-this": [2, "self"],
|
||||
@@ -63,7 +61,6 @@
|
||||
"generator-star-spacing": [0, {"before": false, "after": true}],
|
||||
"global-require": 0,
|
||||
"guard-for-in": 2,
|
||||
"header/header": [2, "line", " Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information."],
|
||||
"id-blacklist": 0,
|
||||
"indent": [2, 4, {"SwitchCase": 0}],
|
||||
"jsx-quotes": [2, "prefer-single"],
|
||||
|
||||
1
.gitignore
vendored
@@ -20,7 +20,6 @@ build/
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
xcshareddata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
|
||||
147
CHANGELOG.md
@@ -1,152 +1,5 @@
|
||||
# Mattermost Mobile Apps Changelog
|
||||
|
||||
## v1.11.0 Release
|
||||
- Release Date: August 16, 2018
|
||||
- Server Versions Supported: Server v4.10+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Searching Archived Channels
|
||||
- Added ability to search for archived channels. Requires Mattermost server v5.2 or later.
|
||||
|
||||
#### Deep Linking
|
||||
- Added the ability for custom builds to open Mattermost links directly in the app rather than the default mobile browser. Learn more in our [documentation](https://docs.mattermost.com/mobile/mobile-faq.html#how-do-i-configure-deep-linking)
|
||||
|
||||
### Improvements
|
||||
- Added profile pop-up to combined system messages.
|
||||
- Force re-entering SSO auth credentials after logout.
|
||||
- Added consecutive posts by the same user.
|
||||
- Added a loading indicator when user info is still loading in the left-hand side.
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed an issue where Android devices showed an incorrect timestamp.
|
||||
- Fixed an issue on Android where the app did not get sent to the background when pressing the hardware back button in the channel screen.
|
||||
- Fixed an issue with video playback when the filename had spaces.
|
||||
- Fixed an issue where the app crashed when playing YouTube videos.
|
||||
- Fixed an issue with session expiration notification.
|
||||
- Fixed an issue with sharing files from Google Drive in Android Share Extension.
|
||||
- Fixed an issue on Android where replying to a push notification sometimes went to the wrong channel.
|
||||
- Fixed an issue where the previous server URL was present on the input textbox before changing the screen to Login.
|
||||
- Fixed an issue where user menu was not translated correctly.
|
||||
- Fixed an issue where some field lengths in Account Settings didn't match the desktop app.
|
||||
- Fixed an issue where long URLs for embedded images in message attachments got cut off and didn't render.
|
||||
- Fixed an issue where link preview images were not cropped properly.
|
||||
- Fixed an issue where long usernames didn't wrap properly in the Account Settings menu.
|
||||
- Fixed an issue where DMs would not open if users were using "Jump To".
|
||||
- Fixed an issue where no message was displayed after removing a user from a channel with join/leave messages disabled.
|
||||
|
||||
## v1.10.0 Release
|
||||
- Release Date: July 16, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Channel drawer performance
|
||||
- Android devices will notice significant performance improvements when opening and closing the channel drawer.
|
||||
|
||||
#### Channel loading performance
|
||||
- Improved channel loading performance as post are retrieved with every push notification
|
||||
|
||||
#### Announcement banner improvements
|
||||
- Markdown now renders when announcement banners are expanded
|
||||
- When enabled by the System Admin, users can now dismiss announcement banners until their next session
|
||||
|
||||
### Improvements
|
||||
|
||||
- Combined consecutive messages from the same user.
|
||||
- Added experimental support for certificate-based authentication (CBA) for iOS to identify a user or a device before granting access to Mattermost. See [documentation](https://docs.mattermost.com/deployment/certificate-based-authentication.html) to learn more.
|
||||
- Added support for the experimental automatic direct message replies feature.
|
||||
- Added support for the experimental timezone feature.
|
||||
- Changed post textbox to not be a connected component.
|
||||
- Allow connecting to mattermost instances hosted at subpaths.
|
||||
- Added support for starting YouTube videos at a given time.
|
||||
- Added support for keeping messages if slash command fails.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed an issue where the unread badge background was always white.
|
||||
- Fixed an issue where a username repeated in system message if user was added to a channel more than once.
|
||||
- Fixed an issue where Android Sharing from Microsoft apps failed.
|
||||
- Fixed an issue where YouTube crashed the app if link did not have a time set.
|
||||
- Fixed an issue where System Admins did not see all teams available to join on mobile.
|
||||
- Fixed an issue where users were unable to share from Files app.
|
||||
- Fixed an issue where viewing a non-existent permalink didn't show an error message.
|
||||
- Fixed an issue where jumping to a channel search did not bold unread channels.
|
||||
- Fixed an issue with being able to add own user to a Group Message channel.
|
||||
- Fixed an issue with not being able to reply from a push notification on iOS.
|
||||
- Fixed an issue where the app did not display Brazilian language.
|
||||
|
||||
## 1.9.3 Release
|
||||
- Release Date: July 04, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed multiple issues causing app crashes
|
||||
- Fixed an issue on iOS devices with typing non-english characters in the post input box
|
||||
|
||||
## 1.9.2 Release
|
||||
- Release Date: June 27, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed an issue where attached videos did not play for the poster
|
||||
- Fixed an issue where "Jump to recent messages" from the permalink view did not direct the user to the bottom of the channel
|
||||
- Fixed an issue where post comments did not identify which parent post they belonged to
|
||||
- Fixed multiple issues with typing non-english characters in the post input box
|
||||
- Fixed multiple issues causing random app crashes
|
||||
- Fixed an issue where files from the Android Files app failed to upload
|
||||
- Fixed an issue where the iOS share extension crashed when switching the team or channel
|
||||
- Fixed an issue where files from the Microsoft app failed to upload
|
||||
- Fixed an issue on Android devices where sharing files changed the file extension of the attachment
|
||||
|
||||
## 1.9.1 Release
|
||||
- Release Date: June 23, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed an issue with typing lag on Android devices
|
||||
- Fixed an issue causing users to be logged out after upgrading to v1.9.0
|
||||
- Fixed an issue where the ``in:`` and ``from:`` modifiers were not being added to the search field
|
||||
|
||||
## v1.9.0 Release
|
||||
- Release Date: June 16, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Improved first load time on Android
|
||||
- Significantly decreased first load time on Android devices from cold start.
|
||||
|
||||
#### iOS Files app support
|
||||
- Added support for attaching files from the iOS Files app from within Mattermost.
|
||||
|
||||
#### Improved styling of push notification
|
||||
- Improved the layout of message content, channel name and sender name in push notifications.
|
||||
|
||||
### Improvements
|
||||
|
||||
- Combined join/leave system messages.
|
||||
- Added splash screen and channel loader improvements.
|
||||
- Removed the desktop notification duration setting.
|
||||
- Added cache team icon and set background to always be white if using a PNG file.
|
||||
- Added whitelabel for icons and splash screen.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed an issue where other user's display name did not render in combined system messages after joining the channel.
|
||||
- Fixed an issue where posts incorrectly had "Commented on Someone's message" above them.
|
||||
- Fixed an issue where deleting a post or its parent in permalink view left permalink view blank.
|
||||
- Fixed an issue where "User is typing" message cut was off.
|
||||
- Fixed an issue where `More New Messages Above` appeared at the top of new channel on joining.
|
||||
- Fixed an issue where a user was not directed to Town Square when leaving a channel.
|
||||
- Fixed an issue where long post were not collapsed on Android.
|
||||
- Fixed an issue where a user's name was initially shown as "someone" when opening a direct message with the user.
|
||||
- Fixed an issue where an error was received when trying to change the team or channel from the share extension.
|
||||
- Fixed an issue where switching to a newly created channel from a push notification redirected a user to Town Square.
|
||||
- Fixed an issue where a public channel made private did not disappear automatically from clients not part of the channel.
|
||||
|
||||
## v1.8.0 Release
|
||||
- Release Date: April 27, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Code Contribution Guidelines
|
||||
|
||||
Thank you for your interest in contributing! Please see the [Mattermost Contribution Guide](https://developers.mattermost.com/contribute/getting-started/) which describes the process for making code contributions across Mattermost projects and [join our "Native Mobile Apps" community channel](https://pre-release.mattermost.com/core/channels/native-mobile-apps) to ask questions from community members and the Mattermost core team.
|
||||
Thank you for your interest in contributing! Please see the [Mattermost Contribution Guide](http://docs.mattermost.com/developer/contribution-guide.html) which describes the process for making code contributions across Mattermost projects and [join our "Native Mobile Apps" community channel](https://pre-release.mattermost.com/core/channels/native-mobile-apps) to ask questions from community members and the Mattermost core team.
|
||||
|
||||
### Review Process for this Repo
|
||||
|
||||
|
||||
24
Makefile
@@ -87,7 +87,7 @@ post-install:
|
||||
@cd ./node_modules/mattermost-redux && npm run build
|
||||
|
||||
start: | pre-run ## Starts the React Native packager server
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start; \
|
||||
else \
|
||||
@@ -139,7 +139,7 @@ prepare-android-build:
|
||||
run: run-ios ## alias for run-ios
|
||||
|
||||
run-ios: | check-device-ios pre-run ## Runs the app on an iOS simulator
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo Running iOS app in development; \
|
||||
if [ ! -z "${SIMULATOR}" ]; then \
|
||||
@@ -158,7 +158,7 @@ run-ios: | check-device-ios pre-run ## Runs the app on an iOS simulator
|
||||
fi
|
||||
|
||||
run-android: | check-device-android pre-run prepare-android-build ## Runs the app on an Android emulator or dev device
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo Running Android app in development; \
|
||||
if [ ! -z ${VARIANT} ]; then \
|
||||
@@ -177,25 +177,25 @@ run-android: | check-device-android pre-run prepare-android-build ## Runs the ap
|
||||
fi
|
||||
|
||||
build-ios: | pre-run check-style ## Creates an iOS build
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo; \
|
||||
fi
|
||||
@echo "Building iOS app"
|
||||
@cd fastlane && BABEL_ENV=production NODE_ENV=production bundle exec fastlane ios build
|
||||
@ps -ef | grep -i "cli.js start" | grep -iv grep | awk '{print $$2}' | xargs kill -9
|
||||
@ps -e | grep -i "cli.js start" | grep -iv grep | awk '{print $$1}' | xargs kill -9
|
||||
|
||||
build-android: | pre-run check-style prepare-android-build ## Creates an Android build
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo; \
|
||||
fi
|
||||
@echo "Building Android app"
|
||||
@cd fastlane && BABEL_ENV=production NODE_ENV=production bundle exec fastlane android build
|
||||
@ps -ef | grep -i "cli.js start" | grep -iv grep | awk '{print $$2}' | xargs kill -9
|
||||
@ps -e | grep -i "cli.js start" | grep -iv grep | awk '{print $$1}' | xargs kill -9
|
||||
|
||||
unsigned-ios: pre-run check-style
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo; \
|
||||
fi
|
||||
@@ -206,17 +206,17 @@ unsigned-ios: pre-run check-style
|
||||
@cd build-ios/ && mkdir -p Payload && cp -R Build/Products/Release-iphoneos/Mattermost.app Payload/ && zip -r Mattermost-unsigned.ipa Payload/
|
||||
@mv build-ios/Mattermost-unsigned.ipa .
|
||||
@rm -rf build-ios/
|
||||
@ps -ef | grep -i "cli.js start" | grep -iv grep | awk '{print $$2}' | xargs kill -9
|
||||
@ps -e | grep -i "cli.js start" | grep -iv grep | awk '{print $$1}' | xargs kill -9
|
||||
|
||||
unsigned-android: pre-run check-style prepare-android-build
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo; \
|
||||
fi
|
||||
@echo "Building unsigned Android app"
|
||||
@cd fastlane && NODE_ENV=production bundle exec fastlane android unsigned
|
||||
@mv android/app/build/outputs/apk/unsigned/app-unsigned-unsigned.apk ./Mattermost-unsigned.apk
|
||||
@ps -ef | grep -i "cli.js start" | grep -iv grep | awk '{print $$2}' | xargs kill -9
|
||||
@mv android/app/build/outputs/apk/app-unsigned-unsigned.apk ./Mattermost-unsigned.apk
|
||||
@ps -e | grep -i "cli.js start" | grep -iv grep | awk '{print $$1}' | xargs kill -9
|
||||
|
||||
test: | pre-run check-style ## Runs tests
|
||||
@npm test
|
||||
|
||||
512
NOTICE.txt
@@ -973,6 +973,25 @@ Contact GitHub API Training Shop Blog About
|
||||
|
||||
---
|
||||
|
||||
## jsdom-global
|
||||
|
||||
jsdom-global will inject document, window and other DOM API into your Node.js environment. Useful for running, in Node.js, tests that are made for browsers.
|
||||
|
||||
* HOMEPAGE
|
||||
* https://github.com/rstacruz/jsdom-global
|
||||
|
||||
* LICENSE
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## url-parse
|
||||
|
||||
This product contains 'url-parse.js', Small footprint URL parser that works seamlessly across Node.js and browser environments.
|
||||
@@ -1038,6 +1057,88 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## NYC
|
||||
|
||||
Istanbul's state of the art command line interface
|
||||
|
||||
* HOMEPAGE
|
||||
* https://github.com/istanbuljs/nyc
|
||||
|
||||
* LICENSE
|
||||
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2015, Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice
|
||||
appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## Enzyme
|
||||
|
||||
JavaScript Testing utilities for React
|
||||
|
||||
* HOMEPAGE
|
||||
* http://airbnb.io/enzyme/
|
||||
|
||||
* LICENSE
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Airbnb, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## Chai
|
||||
|
||||
BDD / TDD assertion framework for node.js and the browser that can be paired with any testing framework.
|
||||
|
||||
* HOMEPAGE
|
||||
* http://chaijs.com/
|
||||
|
||||
* LICENSE
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Chai.js Assertion Library
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## TinyColor
|
||||
|
||||
This product contains 'tinycolor', a small, fast library for color manipulation and conversion in JavaScript. It allows many forms of input, while providing color conversions and other color utility functions
|
||||
@@ -1072,6 +1173,40 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-search-box
|
||||
|
||||
This product contains a modified portion of 'react-native-search-box', a simple search box with animation for React Native.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/crabstudio
|
||||
|
||||
* LICENSE:
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017 Crabstudio.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-exception-handler
|
||||
|
||||
This product contains 'react-native-exception-handler', A react native module that lets you to register a global error handler that can capture fatal/non fatal uncaught exceptions.
|
||||
@@ -1428,6 +1563,40 @@ SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## youtube-video-id
|
||||
|
||||
This product contains 'youtube-video-id', Extracts the YouTube video ID from a url or string.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/remarkablemark/youtube-video-id
|
||||
|
||||
* LICENSE:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Menglin "Mark" Xu <mark@remarkablemark.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-video
|
||||
|
||||
This product contains 'react-native-video', A <Video> component for react-native.
|
||||
@@ -1983,6 +2152,40 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
---
|
||||
|
||||
## socketcluster
|
||||
|
||||
SocketCluster is a fast, highly scalable HTTP + realtime server engine which lets you build multi-process realtime servers that make use of all CPU cores on a machine/instance.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/SocketCluster/socketcluster
|
||||
|
||||
* LICENSE:
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2013-2017 SocketCluster.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## commonmark
|
||||
|
||||
This product contains a modified version of 'commonmark'. CommonMark is a rationalized version of Markdown syntax, with a spec and BSD-licensed reference implementations in C and JavaScript.
|
||||
@@ -2110,6 +2313,219 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE
|
||||
|
||||
---
|
||||
|
||||
## react-native-media-controls
|
||||
|
||||
This product contains a modified version of 'react-native-media-controls' This project is a UI component to manipulate your media.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/charliesbox/react-native-media-controls
|
||||
|
||||
* LICENSE:
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
---
|
||||
|
||||
## react-native-section-list-get-item-layout
|
||||
|
||||
This package provides a function that helps you construct the getItemLayout function for your SectionLists.
|
||||
@@ -2202,99 +2618,3 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---
|
||||
|
||||
## mime-db
|
||||
|
||||
This is a database of all mime types.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/jshttp/mime-db
|
||||
|
||||
* LICENSE:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Jonathan Ong me@jongleberry.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-document-picker
|
||||
|
||||
Document Picker for React Native using Document Providers.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/Elyx0/react-native-document-picker
|
||||
|
||||
* LICENSE:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Elyx0
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-image-gallery
|
||||
|
||||
Pure JavaScript image gallery component for iOS and Android.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/archriss/react-native-image-gallery
|
||||
|
||||
* LICENSE:
|
||||
|
||||
---
|
||||
|
||||
## react-native-keychain
|
||||
|
||||
Keychain Access for React Native.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/oblador/react-native-keychain
|
||||
|
||||
* LICENSE:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Joel Arvidsson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Mattermost Mobile
|
||||
|
||||
- **Supported Server versions:** 4.0+
|
||||
- **Supported iOS versions:** 9.3+
|
||||
- **Supported Android versions:** 5.0+
|
||||
**Supported Server Versions:** 4.0+
|
||||
|
||||
**Supported iOS versions:** 9.3+
|
||||
**Supported Android versions:** 5.0+
|
||||
|
||||
Mattermost is an open source Slack-alternative used by thousands of companies around the world in 14 languages. Learn more at [https://about.mattermost.com](https://about.mattermost.com).
|
||||
|
||||
@@ -10,8 +11,6 @@ You can download our apps from the [App Store](https://about.mattermost.com/matt
|
||||
|
||||
We plan on releasing monthly updates with new features - check the [changelog](https://github.com/mattermost/mattermost-mobile/blob/master/CHANGELOG.md) for what features are currently supported!
|
||||
|
||||
**Important:** If you self-compile the Mattermost Mobile apps you also need to self-compile and deploy your own [Mattermost Push Notification Service](https://github.com/mattermost/mattermost-push-proxy).
|
||||
|
||||
# How to Contribute
|
||||
|
||||
### Testing
|
||||
|
||||
@@ -106,16 +106,15 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 26
|
||||
versionCode 142
|
||||
versionName "1.12.0"
|
||||
multiDexEnabled = true
|
||||
targetSdkVersion 23
|
||||
versionCode 122
|
||||
versionName "1.9.3"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
@@ -152,7 +151,6 @@ android {
|
||||
unsigned.initWith(buildTypes.release)
|
||||
unsigned {
|
||||
signingConfig null
|
||||
matchingFallbacks = ['debug', 'release']
|
||||
}
|
||||
}
|
||||
// applicationVariants are e.g. debug, release
|
||||
@@ -187,37 +185,39 @@ configurations.all {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.android.support:appcompat-v7:27.1.1"
|
||||
implementation 'com.android.support:design:27.1.1'
|
||||
implementation 'com.android.support:percent:27.1.1'
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
implementation project(':react-native-document-picker')
|
||||
implementation project(':react-native-keychain')
|
||||
implementation project(':react-native-doc-viewer')
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-navigation')
|
||||
implementation project(':react-native-image-picker')
|
||||
implementation project(':react-native-bottom-sheet')
|
||||
implementation project(':react-native-device-info')
|
||||
implementation project(':reactnativenotifications')
|
||||
implementation project(':react-native-cookies')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-local-auth')
|
||||
implementation project(':jail-monkey')
|
||||
implementation project(':react-native-youtube')
|
||||
implementation project(':react-native-sentry')
|
||||
implementation project(':react-native-exception-handler')
|
||||
implementation project(':rn-fetch-blob')
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:25.0.1"
|
||||
compile 'com.android.support:percent:25.3.1'
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
compile project(':react-native-document-picker')
|
||||
compile project(':react-native-keychain')
|
||||
compile project(':react-native-doc-viewer')
|
||||
compile project(':react-native-video')
|
||||
compile project(':react-native-navigation')
|
||||
compile project(':react-native-image-picker')
|
||||
compile project(':react-native-bottom-sheet')
|
||||
compile ('com.google.android.gms:play-services-gcm:9.4.0') {
|
||||
force = true;
|
||||
}
|
||||
compile project(':react-native-device-info')
|
||||
compile project(':reactnativenotifications')
|
||||
compile project(':react-native-cookies')
|
||||
compile project(':react-native-linear-gradient')
|
||||
compile project(':react-native-vector-icons')
|
||||
compile project(':react-native-svg')
|
||||
compile project(':react-native-local-auth')
|
||||
compile project(':jail-monkey')
|
||||
compile project(':react-native-youtube')
|
||||
compile project(':react-native-sentry')
|
||||
compile project(':react-native-exception-handler')
|
||||
compile project(':react-native-fetch-blob')
|
||||
|
||||
// For animated GIF support
|
||||
implementation 'com.facebook.fresco:animated-base-support:1.3.0'
|
||||
compile 'com.facebook.fresco:animated-base-support:1.3.0'
|
||||
// For WebP support, including animated WebP
|
||||
implementation 'com.facebook.fresco:animated-gif:1.3.0'
|
||||
implementation 'com.facebook.fresco:animated-webp:1.3.0'
|
||||
implementation 'com.facebook.fresco:webpsupport:1.3.0'
|
||||
compile 'com.facebook.fresco:animated-gif:1.3.0'
|
||||
compile 'com.facebook.fresco:animated-webp:1.3.0'
|
||||
compile 'com.facebook.fresco:webpsupport:1.3.0'
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
|
||||
@@ -5,11 +5,18 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<permission
|
||||
android:name="${applicationId}.permission.C2D_MESSAGE"
|
||||
android:protectionLevel="signature" />
|
||||
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.SEND" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-sdk
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.NotificationChannel;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@@ -141,19 +140,8 @@ public class CustomPushNotification extends PushNotification {
|
||||
String packageName = mContext.getPackageName();
|
||||
NotificationPreferences notificationPreferences = NotificationPreferences.getInstance(mContext);
|
||||
|
||||
String CHANNEL_ID = "channel_01";
|
||||
String CHANNEL_NAME = "Mattermost notifications";
|
||||
|
||||
// First, get a builder initialized with defaults from the core class.
|
||||
final Notification.Builder notification = new Notification.Builder(mContext);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
|
||||
CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
notification.setChannelId(CHANNEL_ID);
|
||||
}
|
||||
Bundle bundle = mNotificationProps.asBundle();
|
||||
String version = bundle.getString("version");
|
||||
|
||||
@@ -283,13 +271,7 @@ public class CustomPushNotification extends PushNotification {
|
||||
replyIntent.setAction(KEY_TEXT_REPLY);
|
||||
replyIntent.putExtra(NOTIFICATION_ID, notificationId);
|
||||
replyIntent.putExtra("pushNotification", bundle);
|
||||
PendingIntent replyPendingIntent;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
replyPendingIntent = PendingIntent.getForegroundService(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
} else {
|
||||
replyPendingIntent = PendingIntent.getService(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
PendingIntent replyPendingIntent = PendingIntent.getService(mContext, 103, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
|
||||
.setLabel("Reply")
|
||||
|
||||
@@ -66,7 +66,7 @@ public class MainApplication extends NavigationApplication implements INotificat
|
||||
new JailMonkeyPackage(),
|
||||
new RNFetchBlobPackage(),
|
||||
new MattermostPackage(this),
|
||||
new RNSentryPackage(),
|
||||
new RNSentryPackage(this),
|
||||
new ReactNativeExceptionHandlerPackage(),
|
||||
new ReactNativeYouTube(),
|
||||
new ReactVideoPackage(),
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.RemoteInput;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.RemoteInput;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
@@ -22,28 +18,6 @@ import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
||||
public class NotificationReplyService extends HeadlessJsTaskService {
|
||||
private Context mContext;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
Context mContext = this.getApplicationContext();
|
||||
final Resources res = mContext.getResources();
|
||||
String packageName = mContext.getPackageName();
|
||||
int smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
|
||||
String CHANNEL_ID = "Reply job";
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
|
||||
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
|
||||
Notification notification =
|
||||
new Notification.Builder(mContext, CHANNEL_ID)
|
||||
.setContentTitle("Replying to message")
|
||||
.setContentText(packageName)
|
||||
.setSmallIcon(smallIconResId)
|
||||
.build();
|
||||
startForeground(1, notification);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
|
||||
mContext = getApplicationContext();
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentResolver;
|
||||
import android.os.Environment;
|
||||
@@ -96,33 +95,15 @@ public class RealPathUtil {
|
||||
|
||||
public static String getPathFromSavingTempFile(Context context, final Uri uri) {
|
||||
File tmpFile;
|
||||
String fileName = null;
|
||||
|
||||
// Try and get the filename from the Uri
|
||||
try {
|
||||
Cursor returnCursor =
|
||||
context.getContentResolver().query(uri, null, null, null, null);
|
||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
returnCursor.moveToFirst();
|
||||
fileName = returnCursor.getString(nameIndex);
|
||||
} catch (Exception e) {
|
||||
// just continue to get the filename with the last segment of the path
|
||||
}
|
||||
|
||||
try {
|
||||
if (fileName == null) {
|
||||
fileName = uri.getLastPathSegment().toString().trim();
|
||||
}
|
||||
|
||||
|
||||
String fileName = uri.getLastPathSegment();
|
||||
File cacheDir = new File(context.getCacheDir(), "mmShare");
|
||||
if (!cacheDir.exists()) {
|
||||
cacheDir.mkdirs();
|
||||
}
|
||||
|
||||
String mimeType = getMimeType(uri.getPath());
|
||||
tmpFile = new File(cacheDir, fileName);
|
||||
tmpFile.createNewFile();
|
||||
tmpFile = File.createTempFile("tmp", fileName, cacheDir);
|
||||
|
||||
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||
|
||||
|
||||
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 459 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 585 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 590 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Executable file → Normal file
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 468 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Executable file → Normal file
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 608 KiB |
@@ -2,12 +2,11 @@
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.0'
|
||||
classpath 'com.google.gms:google-services:3.2.0'
|
||||
classpath 'com.android.tools.build:gradle:2.2.+'
|
||||
classpath 'com.google.gms:google-services:3.1.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@@ -16,7 +15,6 @@ buildscript {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
maven {
|
||||
@@ -29,18 +27,3 @@ allprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
compileSdkVersion = 26
|
||||
buildToolsVersion = '26.0.2'
|
||||
}
|
||||
subprojects { subproject ->
|
||||
afterEvaluate{
|
||||
if((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,10 @@
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx2048M
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.enableAapt2=false
|
||||
android.useDeprecatedNdk=true
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
|
||||
@@ -13,8 +13,8 @@ include ':react-native-sentry'
|
||||
project(':react-native-sentry').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sentry/android')
|
||||
include ':react-native-exception-handler'
|
||||
project(':react-native-exception-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exception-handler/android')
|
||||
include ':rn-fetch-blob'
|
||||
project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android')
|
||||
include ':react-native-fetch-blob'
|
||||
project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android')
|
||||
include ':jail-monkey'
|
||||
project(':jail-monkey').projectDir = new File(rootProject.projectDir, '../node_modules/jail-monkey/android')
|
||||
include ':react-native-local-auth'
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {networkStatusChangedAction} from 'redux-offline';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {DeviceTypes} from 'app/constants';
|
||||
|
||||
export function connection(isOnline) {
|
||||
return async (dispatch) => {
|
||||
Client4.setOnline(isOnline);
|
||||
dispatch(networkStatusChangedAction(isOnline));
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: DeviceTypes.CONNECTION_CHANGED,
|
||||
data: isOnline,
|
||||
});
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
35
app/actions/views/account_notifications.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {updateMe} from 'mattermost-redux/actions/users';
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||
|
||||
export function handleUpdateUserNotifyProps(notifyProps) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const config = state.entities.general.config;
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
const {interval, user_id: userId, ...otherProps} = notifyProps;
|
||||
|
||||
const email = notifyProps.email;
|
||||
if (config.EnableEmailBatching === 'true' && email !== 'false') {
|
||||
const emailInterval = [{
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORY_NOTIFICATIONS,
|
||||
name: Preferences.EMAIL_INTERVAL,
|
||||
value: interval,
|
||||
}];
|
||||
|
||||
savePreferences(currentUserId, emailInterval)(dispatch, getState);
|
||||
}
|
||||
|
||||
const props = {...otherProps, email};
|
||||
try {
|
||||
await updateMe({notify_props: props})(dispatch, getState);
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
@@ -208,7 +208,7 @@ export function loadPostsIfNecessaryWithRetry(channelId) {
|
||||
|
||||
export async function retryGetPostsAction(action, dispatch, getState, maxTries = MAX_POST_TRIES) {
|
||||
for (let i = 0; i < maxTries; i++) {
|
||||
const {data} = await dispatch(action);
|
||||
const {data} = await action(dispatch, getState);
|
||||
|
||||
if (data) {
|
||||
dispatch(setChannelRetryFailed(false));
|
||||
@@ -259,11 +259,8 @@ export function selectInitialChannel(teamId) {
|
||||
const isGMVisible = lastChannel && lastChannel.type === General.GM_CHANNEL &&
|
||||
isGroupChannelVisible(myPreferences, lastChannel);
|
||||
|
||||
if (
|
||||
myMembers[lastChannelId] &&
|
||||
lastChannel &&
|
||||
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)
|
||||
) {
|
||||
if (lastChannelId && myMembers[lastChannelId] &&
|
||||
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)) {
|
||||
handleSelectChannel(lastChannelId)(dispatch, getState);
|
||||
markChannelAsRead(lastChannelId)(dispatch, getState);
|
||||
return;
|
||||
@@ -514,19 +511,17 @@ export function increasePostVisibility(channelId, focusedPostId) {
|
||||
}];
|
||||
|
||||
const posts = result.data;
|
||||
let hasMorePost = false;
|
||||
if (posts) {
|
||||
hasMorePost = posts.order.length >= pageSize;
|
||||
|
||||
// make sure to increment the posts visibility
|
||||
// only if we got results
|
||||
actions.push(doIncreasePostVisibility(channelId));
|
||||
|
||||
actions.push(setLoadMorePostsVisible(hasMorePost));
|
||||
actions.push(setLoadMorePostsVisible(posts.order.length >= pageSize));
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions));
|
||||
return hasMorePost;
|
||||
|
||||
return Boolean(posts);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {addChannelMember} from 'mattermost-redux/actions/channels';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {removeChannelMember} from 'mattermost-redux/actions/channels';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {executeCommand as executeCommandService} from 'mattermost-redux/actions/integrations';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {handleSelectChannel, setChannelDisplayName} from './channel';
|
||||
import {createChannel} from 'mattermost-redux/actions/channels';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {updateMe} from 'mattermost-redux/actions/users';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {uploadProfileImage, updateMe} from 'mattermost-redux/actions/users';
|
||||
import {buildFileUploadData} from 'app/utils/file';
|
||||
|
||||
export function updateUser(user, success, error) {
|
||||
return async (dispatch, getState) => {
|
||||
@@ -18,14 +17,9 @@ export function updateUser(user, success, error) {
|
||||
};
|
||||
}
|
||||
|
||||
export function setProfileImageUri(imageUri = '') {
|
||||
return {
|
||||
type: ViewTypes.SET_PROFILE_IMAGE_URI,
|
||||
imageUri,
|
||||
export function handleUploadProfileImage(image, userId) {
|
||||
return async (dispatch, getState) => {
|
||||
const imageData = buildFileUploadData(image);
|
||||
return await uploadProfileImage(userId, imageData)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
updateUser,
|
||||
setProfileImageUri,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {addReaction as serviceAddReaction} from 'mattermost-redux/actions/posts';
|
||||
import {getPostIdsInCurrentChannel, makeGetPostIdsForThread} from 'mattermost-redux/selectors/entities/posts';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {FileTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {getDataRetentionPolicy} from 'mattermost-redux/actions/general';
|
||||
import {GeneralTypes} from 'mattermost-redux/action_types';
|
||||
import {getSessions} from 'mattermost-redux/actions/users';
|
||||
import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {app} from 'app/mattermost';
|
||||
import {getDeviceTimezone, isTimezoneEnabled} from 'app/utils/timezone';
|
||||
|
||||
export function handleLoginIdChanged(loginId) {
|
||||
return async (dispatch, getState) => {
|
||||
@@ -34,8 +30,7 @@ export function handlePasswordChanged(password) {
|
||||
export function handleSuccessfulLogin() {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const config = getConfig(state);
|
||||
const license = getLicense(state);
|
||||
const {config, license} = state.entities.general;
|
||||
const token = Client4.getToken();
|
||||
const url = Client4.getUrl();
|
||||
const deviceToken = state.entities.general.deviceToken;
|
||||
@@ -43,11 +38,6 @@ export function handleSuccessfulLogin() {
|
||||
|
||||
app.setAppCredentials(deviceToken, currentUserId, token, url);
|
||||
|
||||
const enableTimezone = isTimezoneEnabled(state);
|
||||
if (enableTimezone) {
|
||||
dispatch(autoUpdateTimezone(getDeviceTimezone()));
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: GeneralTypes.RECEIVED_APP_CREDENTIALS,
|
||||
data: {
|
||||
@@ -71,27 +61,18 @@ export function getSession() {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {currentUserId} = state.entities.users;
|
||||
const {deviceToken} = state.entities.general;
|
||||
const {credentials} = state.entities.general;
|
||||
const token = credentials && credentials.token;
|
||||
|
||||
if (!currentUserId || !deviceToken) {
|
||||
return 0;
|
||||
if (currentUserId && token) {
|
||||
const session = await Client4.getSessions(currentUserId, token);
|
||||
if (Array.isArray(session) && session[0]) {
|
||||
const s = session[0];
|
||||
return s.expires_at;
|
||||
}
|
||||
}
|
||||
|
||||
let sessions;
|
||||
try {
|
||||
sessions = await dispatch(getSessions(currentUserId));
|
||||
} catch (e) {
|
||||
console.warn('Failed to get current session', e); // eslint-disable-line no-console
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!Array.isArray(sessions.data)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const session = sessions.data.find((s) => s.device_id === deviceToken);
|
||||
|
||||
return session && session.expires_at ? session.expires_at : 0;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
handlePasswordChanged,
|
||||
} from 'app/actions/views/login';
|
||||
|
||||
jest.mock('rn-fetch-blob/fs', () => ({
|
||||
jest.mock('react-native-fetch-blob/fs', () => ({
|
||||
dirs: {
|
||||
DocumentDir: () => jest.fn(),
|
||||
CacheDir: () => jest.fn(),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {getDirectChannelName} from 'mattermost-redux/utils/channel_utils';
|
||||
import {createDirectChannel, createGroupChannel} from 'mattermost-redux/actions/channels';
|
||||
import {getProfilesByIds, getStatusesByIds} from 'mattermost-redux/actions/users';
|
||||
import {handleSelectChannel, toggleDMChannel, toggleGMChannel} from 'app/actions/views/channel';
|
||||
|
||||
export function makeDirectChannel(otherUserId, switchToChannel = true) {
|
||||
export function makeDirectChannel(otherUserId) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {currentUserId} = state.entities.users;
|
||||
@@ -23,11 +23,11 @@ export function makeDirectChannel(otherUserId, switchToChannel = true) {
|
||||
|
||||
dispatch(toggleDMChannel(otherUserId, 'true', channel.id));
|
||||
} else {
|
||||
result = await dispatch(createDirectChannel(currentUserId, otherUserId));
|
||||
result = await createDirectChannel(currentUserId, otherUserId)(dispatch, getState);
|
||||
channel = result.data;
|
||||
}
|
||||
|
||||
if (channel && switchToChannel) {
|
||||
if (channel) {
|
||||
dispatch(handleSelectChannel(channel.id));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {Posts} from 'mattermost-redux/constants';
|
||||
import {PostTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {GeneralTypes, PostTypes} from 'mattermost-redux/action_types';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {fetchMyChannelsAndMembers, markChannelAsRead} from 'mattermost-redux/actions/channels';
|
||||
import {getClientConfig, getDataRetentionPolicy, getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||
import {getPosts} from 'mattermost-redux/actions/posts';
|
||||
import {getMyTeams, getMyTeamMembers, selectTeam} from 'mattermost-redux/actions/teams';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
@@ -14,6 +15,7 @@ import {recordTime} from 'app/utils/segment';
|
||||
import {
|
||||
handleSelectChannel,
|
||||
setChannelDisplayName,
|
||||
retryGetPostsAction,
|
||||
} from 'app/actions/views/channel';
|
||||
|
||||
export function startDataCleanup() {
|
||||
@@ -55,15 +57,10 @@ export function loadFromPushNotification(notification) {
|
||||
const {data} = notification;
|
||||
const {currentTeamId, teams, myMembers: myTeamMembers} = state.entities.teams;
|
||||
const {currentChannelId, channels} = state.entities.channels;
|
||||
const channelId = data.channel_id;
|
||||
|
||||
let channelId = '';
|
||||
let teamId = currentTeamId;
|
||||
if (data) {
|
||||
channelId = data.channel_id;
|
||||
|
||||
// when the notification does not have a team id is because its from a DM or GM
|
||||
teamId = data.team_id || currentTeamId;
|
||||
}
|
||||
// when the notification does not have a team id is because its from a DM or GM
|
||||
const teamId = data.team_id || currentTeamId;
|
||||
|
||||
// load any missing data
|
||||
const loading = [];
|
||||
@@ -86,10 +83,12 @@ export function loadFromPushNotification(notification) {
|
||||
dispatch(selectTeam({id: teamId}));
|
||||
}
|
||||
|
||||
// mark channel as read
|
||||
dispatch(markChannelAsRead(channelId, channelId === currentChannelId ? null : currentChannelId, false));
|
||||
|
||||
if (channelId !== currentChannelId) {
|
||||
// when the notification is from the same channel as the current channel
|
||||
// we should get the posts
|
||||
if (channelId === currentChannelId) {
|
||||
dispatch(markChannelAsRead(channelId, null, false));
|
||||
await retryGetPostsAction(getPosts(channelId), dispatch, getState);
|
||||
} else {
|
||||
// when the notification is from a channel other than the current channel
|
||||
dispatch(markChannelAsRead(channelId, currentChannelId, false));
|
||||
dispatch(setChannelDisplayName(''));
|
||||
@@ -102,9 +101,7 @@ export function purgeOfflineStore() {
|
||||
return {type: General.OFFLINE_STORE_PURGE};
|
||||
}
|
||||
|
||||
// A non-optimistic version of the createPost action in mattermost-redux with the file handling
|
||||
// removed since it's not needed.
|
||||
export function createPostForNotificationReply(post) {
|
||||
export function createPost(post) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const currentUserId = state.entities.users.currentUserId;
|
||||
@@ -142,13 +139,6 @@ export function recordLoadTime(screenName, category) {
|
||||
};
|
||||
}
|
||||
|
||||
export function setDeepLinkURL(url) {
|
||||
return {
|
||||
type: ViewTypes.SET_DEEP_LINK_URL,
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
loadConfigAndLicense,
|
||||
loadFromPushNotification,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
import {GeneralTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
import configureStore from 'redux-mock-store';
|
||||
@@ -11,7 +11,7 @@ import {ViewTypes} from 'app/constants';
|
||||
|
||||
import {handleServerUrlChanged} from 'app/actions/views/select_server';
|
||||
|
||||
jest.mock('rn-fetch-blob/fs', () => ({
|
||||
jest.mock('react-native-fetch-blob/fs', () => ({
|
||||
dirs: {
|
||||
DocumentDir: () => jest.fn(),
|
||||
CacheDir: () => jest.fn(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
@@ -7,12 +7,10 @@ import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/c
|
||||
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
|
||||
import {setChannelDisplayName} from './channel';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
export function handleTeamChange(teamId, selectChannel = true) {
|
||||
return async (dispatch, getState) => {
|
||||
@@ -42,7 +40,7 @@ export function selectDefaultTeam() {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
||||
const {ExperimentalPrimaryTeam} = getConfig(state);
|
||||
const {ExperimentalPrimaryTeam} = state.entities.general.config;
|
||||
const {teams: allTeams, myMembers} = state.entities.teams;
|
||||
const teams = Object.keys(myMembers).map((key) => allTeams[key]);
|
||||
|
||||
@@ -57,8 +55,6 @@ export function selectDefaultTeam() {
|
||||
|
||||
if (defaultTeam) {
|
||||
handleTeamChange(defaultTeam.id)(dispatch, getState);
|
||||
} else if (state.requests.teams.getTeams.status === RequestStatus.FAILURE || state.requests.teams.getMyTeams.status === RequestStatus.FAILURE) {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_ERROR_TEAMS);
|
||||
} else {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_NO_TEAMS);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
handleCommentDraftSelectionChanged,
|
||||
} from 'app/actions/views/thread';
|
||||
|
||||
jest.mock('rn-fetch-blob/fs', () => ({
|
||||
jest.mock('react-native-fetch-blob/fs', () => ({
|
||||
dirs: {
|
||||
DocumentDir: () => jest.fn(),
|
||||
CacheDir: () => jest.fn(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {userTyping as wsUserTyping} from 'mattermost-redux/actions/websocket';
|
||||
|
||||
|
||||
60
app/app.js
@@ -1,15 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
/* eslint-disable global-require*/
|
||||
import {AsyncStorage, Linking, NativeModules, Platform} from 'react-native';
|
||||
import {AsyncStorage, NativeModules} from 'react-native';
|
||||
import {setGenericPassword, getGenericPassword, resetGenericPassword} from 'react-native-keychain';
|
||||
|
||||
import {loadMe} from 'mattermost-redux/actions/users';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {setDeepLinkURL} from 'app/actions/views/root';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
@@ -51,17 +50,6 @@ export default class App {
|
||||
this.token = null;
|
||||
this.url = null;
|
||||
|
||||
// Load polyfill for iOS 9
|
||||
if (Platform.OS === 'ios') {
|
||||
const majorVersionIOS = parseInt(Platform.Version, 10);
|
||||
if (majorVersionIOS < 10) {
|
||||
require('babel-polyfill');
|
||||
}
|
||||
}
|
||||
|
||||
// Usage deeplinking
|
||||
Linking.addEventListener('url', this.handleDeepLink);
|
||||
|
||||
this.getStartupThemes();
|
||||
this.getAppCredentials();
|
||||
}
|
||||
@@ -103,17 +91,12 @@ export default class App {
|
||||
const [deviceToken, currentUserId] = usernameParsed;
|
||||
const [token, url] = passwordParsed;
|
||||
|
||||
// if for any case the url and the token aren't valid proceed with re-hydration
|
||||
if (url && url !== 'undefined' && token && token !== 'undefined') {
|
||||
this.deviceToken = deviceToken;
|
||||
this.currentUserId = currentUserId;
|
||||
this.token = token;
|
||||
this.url = url;
|
||||
Client4.setUrl(url);
|
||||
Client4.setToken(token);
|
||||
} else {
|
||||
this.waitForRehydration = true;
|
||||
}
|
||||
this.deviceToken = deviceToken;
|
||||
this.currentUserId = currentUserId;
|
||||
this.token = token;
|
||||
this.url = url;
|
||||
Client4.setUrl(url);
|
||||
Client4.setToken(token);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -169,7 +152,6 @@ export default class App {
|
||||
if (!currentUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = `${deviceToken}, ${currentUserId}`;
|
||||
const password = `${token},${url}`;
|
||||
|
||||
@@ -179,14 +161,7 @@ export default class App {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
// Only save to keychain if the url and token are set
|
||||
if (url && token) {
|
||||
try {
|
||||
setGenericPassword(username, password);
|
||||
} catch (e) {
|
||||
console.warn('could not set credentials', e); //eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
setGenericPassword(username, password);
|
||||
};
|
||||
|
||||
setStartupThemes = (toolbarBackground, toolbarTextColor, appBackground) => {
|
||||
@@ -240,11 +215,6 @@ export default class App {
|
||||
]);
|
||||
};
|
||||
|
||||
handleDeepLink = (event) => {
|
||||
const {url} = event;
|
||||
store.dispatch(setDeepLinkURL(url));
|
||||
}
|
||||
|
||||
launchApp = async () => {
|
||||
const shouldStart = await handleManagedConfig();
|
||||
if (shouldStart) {
|
||||
@@ -259,21 +229,11 @@ export default class App {
|
||||
|
||||
const {dispatch} = store;
|
||||
|
||||
Linking.getInitialURL().then((url) => {
|
||||
dispatch(setDeepLinkURL(url));
|
||||
});
|
||||
|
||||
let screen = 'SelectServer';
|
||||
if (this.token && this.url) {
|
||||
screen = 'Channel';
|
||||
tracker.initialLoad = Date.now();
|
||||
|
||||
try {
|
||||
dispatch(loadMe());
|
||||
} catch (e) {
|
||||
// Fall through since we should have a previous version of the current user because we have a token
|
||||
console.warn('Failed to load current user when starting on Channel screen', e); // eslint-disable-line no-console
|
||||
}
|
||||
dispatch(loadMe());
|
||||
}
|
||||
|
||||
switch (screen) {
|
||||
|
||||
@@ -5,13 +5,17 @@ ShallowWrapper {
|
||||
"length": 1,
|
||||
Symbol(enzyme.__root__): [Circular],
|
||||
Symbol(enzyme.__unrendered__): <AnnouncementBanner
|
||||
actions={
|
||||
Object {
|
||||
"dismissBanner": [MockFunction],
|
||||
}
|
||||
}
|
||||
allowDismissal={true}
|
||||
bannerColor="#ddd"
|
||||
bannerDismissed={false}
|
||||
bannerEnabled={true}
|
||||
bannerText="Banner Text"
|
||||
bannerTextColor="#fff"
|
||||
navigator={Object {}}
|
||||
theme={Object {}}
|
||||
/>,
|
||||
Symbol(enzyme.__renderer__): Object {
|
||||
"batchedUpdates": [Function],
|
||||
@@ -54,9 +58,7 @@ ShallowWrapper {
|
||||
]
|
||||
}
|
||||
>
|
||||
<RemoveMarkdown
|
||||
value="Banner Text"
|
||||
/>
|
||||
Banner Text
|
||||
</Text>
|
||||
<Icon
|
||||
allowFontScaling={false}
|
||||
@@ -105,9 +107,7 @@ ShallowWrapper {
|
||||
]
|
||||
}
|
||||
>
|
||||
<RemoveMarkdown
|
||||
value="Banner Text"
|
||||
/>
|
||||
Banner Text
|
||||
</Text>,
|
||||
<Icon
|
||||
allowFontScaling={false}
|
||||
@@ -132,9 +132,7 @@ ShallowWrapper {
|
||||
"props": Object {
|
||||
"accessible": true,
|
||||
"allowFontScaling": true,
|
||||
"children": <RemoveMarkdown
|
||||
value="Banner Text"
|
||||
/>,
|
||||
"children": "Banner Text",
|
||||
"ellipsizeMode": "tail",
|
||||
"numberOfLines": 1,
|
||||
"style": Array [
|
||||
@@ -149,17 +147,7 @@ ShallowWrapper {
|
||||
],
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"value": "Banner Text",
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
"rendered": "Banner Text",
|
||||
"type": [Function],
|
||||
},
|
||||
Object {
|
||||
@@ -216,9 +204,7 @@ ShallowWrapper {
|
||||
]
|
||||
}
|
||||
>
|
||||
<RemoveMarkdown
|
||||
value="Banner Text"
|
||||
/>
|
||||
Banner Text
|
||||
</Text>
|
||||
<Icon
|
||||
allowFontScaling={false}
|
||||
@@ -267,9 +253,7 @@ ShallowWrapper {
|
||||
]
|
||||
}
|
||||
>
|
||||
<RemoveMarkdown
|
||||
value="Banner Text"
|
||||
/>
|
||||
Banner Text
|
||||
</Text>,
|
||||
<Icon
|
||||
allowFontScaling={false}
|
||||
@@ -294,9 +278,7 @@ ShallowWrapper {
|
||||
"props": Object {
|
||||
"accessible": true,
|
||||
"allowFontScaling": true,
|
||||
"children": <RemoveMarkdown
|
||||
value="Banner Text"
|
||||
/>,
|
||||
"children": "Banner Text",
|
||||
"ellipsizeMode": "tail",
|
||||
"numberOfLines": 1,
|
||||
"style": Array [
|
||||
@@ -311,17 +293,7 @@ ShallowWrapper {
|
||||
],
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"value": "Banner Text",
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
"rendered": "Banner Text",
|
||||
"type": [Function],
|
||||
},
|
||||
Object {
|
||||
@@ -359,13 +331,17 @@ ShallowWrapper {
|
||||
"length": 1,
|
||||
Symbol(enzyme.__root__): [Circular],
|
||||
Symbol(enzyme.__unrendered__): <AnnouncementBanner
|
||||
actions={
|
||||
Object {
|
||||
"dismissBanner": [MockFunction],
|
||||
}
|
||||
}
|
||||
allowDismissal={true}
|
||||
bannerColor="#ddd"
|
||||
bannerDismissed={false}
|
||||
bannerEnabled={false}
|
||||
bannerText="Banner Text"
|
||||
bannerTextColor="#fff"
|
||||
navigator={Object {}}
|
||||
theme={Object {}}
|
||||
/>,
|
||||
Symbol(enzyme.__renderer__): Object {
|
||||
"batchedUpdates": [Function],
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Alert,
|
||||
Animated,
|
||||
StyleSheet,
|
||||
Text,
|
||||
@@ -12,19 +13,19 @@ import {
|
||||
import {intlShape} from 'react-intl';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import RemoveMarkdown from 'app/components/remove_markdown';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
|
||||
export default class AnnouncementBanner extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
dismissBanner: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
allowDismissal: PropTypes.bool,
|
||||
bannerColor: PropTypes.string,
|
||||
bannerDismissed: PropTypes.bool,
|
||||
bannerEnabled: PropTypes.bool,
|
||||
bannerText: PropTypes.string,
|
||||
bannerTextColor: PropTypes.string,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@@ -51,24 +52,30 @@ export default class AnnouncementBanner extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handlePress = () => {
|
||||
const {navigator, theme} = this.props;
|
||||
handleDismiss = () => {
|
||||
const {actions, bannerText} = this.props;
|
||||
actions.dismissBanner(bannerText);
|
||||
};
|
||||
|
||||
navigator.push({
|
||||
screen: 'ExpandedAnnouncementBanner',
|
||||
title: this.context.intl.formatMessage({
|
||||
id: 'mobile.announcement_banner.title',
|
||||
defaultMessage: 'Announcement',
|
||||
}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
});
|
||||
handlePress = () => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const options = [{
|
||||
text: formatMessage({id: 'mobile.announcement_banner.ok', defaultMessage: 'OK'}),
|
||||
}];
|
||||
|
||||
if (this.props.allowDismissal) {
|
||||
options.push({
|
||||
text: formatMessage({id: 'mobile.announcement_banner.dismiss', defaultMessage: 'Dismiss'}),
|
||||
onPress: this.handleDismiss,
|
||||
});
|
||||
}
|
||||
|
||||
Alert.alert(
|
||||
formatMessage({id: 'mobile.announcement_banner.title', defaultMessage: 'Announcement'}),
|
||||
this.props.bannerText,
|
||||
options,
|
||||
{cancelable: false}
|
||||
);
|
||||
};
|
||||
|
||||
toggleBanner = (show = true) => {
|
||||
@@ -113,7 +120,7 @@ export default class AnnouncementBanner extends PureComponent {
|
||||
numberOfLines={1}
|
||||
style={[style.bannerText, bannerTextStyle]}
|
||||
>
|
||||
<RemoveMarkdown value={bannerText}/>
|
||||
{bannerText}
|
||||
</Text>
|
||||
<MaterialIcons
|
||||
color={bannerTextColor}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {configure, shallow} from 'enzyme';
|
||||
@@ -12,13 +12,15 @@ jest.useFakeTimers();
|
||||
|
||||
describe('AnnouncementBanner', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
dismissBanner: jest.fn(),
|
||||
},
|
||||
allowDismissal: true,
|
||||
bannerColor: '#ddd',
|
||||
bannerDismissed: false,
|
||||
bannerEnabled: true,
|
||||
bannerText: 'Banner Text',
|
||||
bannerTextColor: '#fff',
|
||||
navigator: {},
|
||||
theme: {},
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
@@ -31,4 +33,16 @@ describe('AnnouncementBanner', () => {
|
||||
wrapper.setProps({bannerEnabled: false});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('should call actions.dismissBanner on handleDismiss', () => {
|
||||
const actions = {dismissBanner: jest.fn()};
|
||||
const props = {...baseProps, actions};
|
||||
const wrapper = shallow(
|
||||
<AnnouncementBanner {...props}/>
|
||||
);
|
||||
|
||||
wrapper.instance().handleDismiss();
|
||||
expect(actions.dismissBanner).toHaveBeenCalledTimes(1);
|
||||
expect(actions.dismissBanner).toHaveBeenCalledWith(props.bannerText);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {dismissBanner} from 'app/actions/views/announcement';
|
||||
|
||||
import AnnouncementBanner from './announcement_banner';
|
||||
|
||||
@@ -14,13 +16,21 @@ function mapStateToProps(state) {
|
||||
const {announcement} = state.views;
|
||||
|
||||
return {
|
||||
allowDismissal: config.AllowBannerDismissal === 'true',
|
||||
bannerColor: config.BannerColor,
|
||||
bannerDismissed: config.BannerText === announcement,
|
||||
bannerEnabled: config.EnableBanner === 'true' && license.IsLicensed === 'true',
|
||||
bannerText: config.BannerText,
|
||||
bannerTextColor: config.BannerTextColor || '#000',
|
||||
theme: getTheme(state),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(AnnouncementBanner);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
dismissBanner,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -8,8 +8,6 @@ import {intlShape} from 'react-intl';
|
||||
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
|
||||
@@ -27,10 +25,6 @@ export default class AtMention extends React.PureComponent {
|
||||
usersByUsername: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onLongPress: emptyFunction,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
};
|
||||
@@ -80,7 +74,7 @@ export default class AtMention extends React.PureComponent {
|
||||
};
|
||||
|
||||
getUserDetailsFromMentionName(props) {
|
||||
let mentionName = props.mentionName.toLowerCase();
|
||||
let mentionName = props.mentionName;
|
||||
|
||||
while (mentionName.length > 0) {
|
||||
if (props.usersByUsername.hasOwnProperty(mentionName)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -16,7 +16,6 @@ import AtMention from './at_mention';
|
||||
import ChannelMention from './channel_mention';
|
||||
import EmojiSuggestion from './emoji_suggestion';
|
||||
import SlashSuggestion from './slash_suggestion';
|
||||
import DateSuggestion from './date_suggestion';
|
||||
|
||||
export default class Autocomplete extends PureComponent {
|
||||
static propTypes = {
|
||||
@@ -27,13 +26,11 @@ export default class Autocomplete extends PureComponent {
|
||||
isSearch: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
enableDateSuggestion: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isSearch: false,
|
||||
cursorPosition: 0,
|
||||
enableDateSuggestion: false,
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -41,7 +38,6 @@ export default class Autocomplete extends PureComponent {
|
||||
channelMentionCount: 0,
|
||||
emojiCount: 0,
|
||||
commandCount: 0,
|
||||
dateCount: 0,
|
||||
keyboardOffset: 0,
|
||||
};
|
||||
|
||||
@@ -61,10 +57,6 @@ export default class Autocomplete extends PureComponent {
|
||||
this.setState({commandCount});
|
||||
};
|
||||
|
||||
handleIsDateFilterChange = (dateCount) => {
|
||||
this.setState({dateCount});
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow);
|
||||
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide);
|
||||
@@ -93,6 +85,10 @@ export default class Autocomplete extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
const wrapperStyle = [];
|
||||
@@ -105,8 +101,8 @@ export default class Autocomplete extends PureComponent {
|
||||
}
|
||||
|
||||
// We always need to render something, but we only draw the borders when we have results to show
|
||||
const {atMentionCount, channelMentionCount, emojiCount, commandCount, dateCount} = this.state;
|
||||
if (atMentionCount + channelMentionCount + emojiCount + commandCount + dateCount > 0) {
|
||||
const {atMentionCount, channelMentionCount, emojiCount, commandCount} = this.state;
|
||||
if (atMentionCount + channelMentionCount + emojiCount + commandCount > 0) {
|
||||
if (this.props.isSearch) {
|
||||
wrapperStyle.push(style.bordersSearch);
|
||||
} else {
|
||||
@@ -114,7 +110,6 @@ export default class Autocomplete extends PureComponent {
|
||||
}
|
||||
}
|
||||
const listHeight = this.listHeight();
|
||||
|
||||
return (
|
||||
<View style={wrapperStyle}>
|
||||
<View style={containerStyle}>
|
||||
@@ -136,12 +131,6 @@ export default class Autocomplete extends PureComponent {
|
||||
onResultCountChange={this.handleCommandCountChange}
|
||||
{...this.props}
|
||||
/>
|
||||
{(this.props.isSearch && this.props.enableDateSuggestion) &&
|
||||
<DateSuggestion
|
||||
onResultCountChange={this.handleIsDateFilterChange}
|
||||
{...this.props}
|
||||
/>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -24,13 +24,11 @@ export default class ChannelMention extends PureComponent {
|
||||
listHeight: PropTypes.number,
|
||||
matchTerm: PropTypes.string,
|
||||
myChannels: PropTypes.array,
|
||||
myMembers: PropTypes.object,
|
||||
otherChannels: PropTypes.array,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
onResultCountChange: PropTypes.func.isRequired,
|
||||
privateChannels: PropTypes.array,
|
||||
publicChannels: PropTypes.array,
|
||||
deletedPublicChannels: PropTypes.instanceOf(Set),
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
@@ -50,7 +48,7 @@ export default class ChannelMention extends PureComponent {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus, myMembers, deletedPublicChannels} = nextProps;
|
||||
const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus} = nextProps;
|
||||
|
||||
if ((matchTerm !== this.props.matchTerm && matchTerm === null) || this.state.mentionComplete) {
|
||||
// if the term changes but is null or the mention has been completed we render this component as null
|
||||
@@ -76,8 +74,7 @@ export default class ChannelMention extends PureComponent {
|
||||
|
||||
if (requestStatus !== RequestStatus.STARTED &&
|
||||
(myChannels !== this.props.myChannels || otherChannels !== this.props.otherChannels ||
|
||||
privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels ||
|
||||
myMembers !== this.props.myMembers || deletedPublicChannels !== this.props.deletedPublicChannels)) {
|
||||
privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels)) {
|
||||
// if the request is complete and the term is not null we show the autocomplete
|
||||
const sections = [];
|
||||
if (isSearch) {
|
||||
@@ -85,7 +82,7 @@ export default class ChannelMention extends PureComponent {
|
||||
sections.push({
|
||||
id: 'suggestion.search.public',
|
||||
defaultMessage: 'Public Channels',
|
||||
data: publicChannels.filter((cId) => !deletedPublicChannels.has(cId) || myMembers[cId]),
|
||||
data: publicChannels,
|
||||
key: 'publicChannels',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {searchChannels} from 'mattermost-redux/actions/channels';
|
||||
import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {
|
||||
@@ -13,7 +12,6 @@ import {
|
||||
filterOtherChannels,
|
||||
filterPublicChannels,
|
||||
filterPrivateChannels,
|
||||
getDeletedPublicChannelsIds,
|
||||
getMatchTermForChannelMention,
|
||||
} from 'app/selectors/autocomplete';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
@@ -40,10 +38,8 @@ function mapStateToProps(state, ownProps) {
|
||||
|
||||
return {
|
||||
myChannels,
|
||||
myMembers: getMyChannelMemberships(state),
|
||||
otherChannels,
|
||||
publicChannels,
|
||||
deletedPublicChannels: getDeletedPublicChannelsIds(state),
|
||||
privateChannels,
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
matchTerm,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import {Dimensions, Platform, StyleSheet} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import {CalendarList} from 'react-native-calendars';
|
||||
|
||||
import {memoizeResult} from 'mattermost-redux/utils/helpers';
|
||||
|
||||
import {DATE_MENTION_SEARCH_REGEX, ALL_SEARCH_FLAGS_REGEX} from 'app/constants/autocomplete';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
|
||||
export default class DateSuggestion extends PureComponent {
|
||||
static propTypes = {
|
||||
cursorPosition: PropTypes.number.isRequired,
|
||||
listHeight: PropTypes.number,
|
||||
matchTerm: PropTypes.string,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
onResultCountChange: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
enableDateSuggestion: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
mentionComplete: false,
|
||||
sections: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {matchTerm} = nextProps;
|
||||
|
||||
if ((matchTerm !== this.props.matchTerm && matchTerm === null) || this.state.mentionComplete) {
|
||||
// if the term changes but is null or the mention has been completed we render this component as null
|
||||
this.setState({
|
||||
mentionComplete: false,
|
||||
sections: [],
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(0);
|
||||
}
|
||||
}
|
||||
|
||||
completeMention = (day) => {
|
||||
const mention = day.dateString;
|
||||
const {cursorPosition, onChangeText, value} = this.props;
|
||||
const mentionPart = value.substring(0, cursorPosition);
|
||||
const flags = mentionPart.match(ALL_SEARCH_FLAGS_REGEX);
|
||||
const currentFlag = flags[flags.length - 1];
|
||||
let completedDraft = mentionPart.replace(DATE_MENTION_SEARCH_REGEX, `${currentFlag} ${mention} `);
|
||||
|
||||
if (value.length > cursorPosition) {
|
||||
completedDraft += value.substring(cursorPosition);
|
||||
}
|
||||
|
||||
onChangeText(completedDraft, true);
|
||||
this.props.onResultCountChange(1);
|
||||
this.setState({mentionComplete: true});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {mentionComplete} = this.state;
|
||||
const {matchTerm, enableDateSuggestion, theme} = this.props;
|
||||
|
||||
if (matchTerm === null || mentionComplete || !enableDateSuggestion) {
|
||||
// If we are not in an active state or the mention has been completed return null so nothing is rendered
|
||||
// other components are not blocked.
|
||||
return null;
|
||||
}
|
||||
|
||||
const currentDate = (new Date()).toDateString();
|
||||
const calendarStyle = calendarTheme(theme);
|
||||
|
||||
return (
|
||||
<CalendarList
|
||||
style={styles.calList}
|
||||
current={currentDate}
|
||||
pastScrollRange={24}
|
||||
futureScrollRange={0}
|
||||
scrollingEnabled={true}
|
||||
pagingEnabled={true}
|
||||
hideArrows={false}
|
||||
horizontal={true}
|
||||
showScrollIndicator={true}
|
||||
onDayPress={this.completeMention}
|
||||
showWeekNumbers={false}
|
||||
theme={calendarStyle}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getDateFontSize = () => {
|
||||
let fontSize = 14;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
const {height, width} = Dimensions.get('window');
|
||||
if (height < 375 || width < 375) {
|
||||
fontSize = 13;
|
||||
}
|
||||
}
|
||||
|
||||
return fontSize;
|
||||
};
|
||||
|
||||
const calendarTheme = memoizeResult((theme) => ({
|
||||
calendarBackground: theme.centerChannelBg,
|
||||
monthTextColor: changeOpacity(theme.centerChannelColor, 0.8),
|
||||
dayTextColor: theme.centerChannelColor,
|
||||
textSectionTitleColor: changeOpacity(theme.centerChannelColor, 0.25),
|
||||
'stylesheet.day.basic': {
|
||||
base: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
alignItems: 'center',
|
||||
},
|
||||
text: {
|
||||
marginTop: 0,
|
||||
fontSize: getDateFontSize(),
|
||||
fontWeight: '300',
|
||||
color: theme.centerChannelColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0)',
|
||||
lineHeight: 23,
|
||||
},
|
||||
today: {
|
||||
backgroundColor: theme.buttonBg,
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 12,
|
||||
},
|
||||
todayText: {
|
||||
color: theme.buttonColor,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
calList: {
|
||||
height: 1700,
|
||||
paddingTop: 5,
|
||||
},
|
||||
});
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {makeGetMatchTermForDateMention} from 'app/selectors/autocomplete';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import DateSuggestion from './date_suggestion';
|
||||
|
||||
function makeMapStateToProps() {
|
||||
const getMatchTermForDateMention = makeGetMatchTermForDateMention();
|
||||
|
||||
return (state, ownProps) => {
|
||||
const {cursorPosition, value} = ownProps;
|
||||
|
||||
const newValue = value.substring(0, cursorPosition);
|
||||
const matchTerm = getMatchTermForDateMention(newValue);
|
||||
|
||||
return {
|
||||
matchTerm,
|
||||
theme: getTheme(state),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps)(DateSuggestion);
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {createSelector} from 'reselect';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -124,6 +124,7 @@ export default class SlashSuggestion extends Component {
|
||||
|
||||
renderItem = ({item}) => (
|
||||
<SlashSuggestionItem
|
||||
displayName={item.display_name}
|
||||
description={item.auto_complete_desc}
|
||||
hint={item.auto_complete_hint}
|
||||
onPress={this.completeSuggestion}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -12,6 +12,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class SlashSuggestionItem extends PureComponent {
|
||||
static propTypes = {
|
||||
displayName: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
@@ -26,6 +27,7 @@ export default class SlashSuggestionItem extends PureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
displayName,
|
||||
description,
|
||||
hint,
|
||||
theme,
|
||||
@@ -39,7 +41,7 @@ export default class SlashSuggestionItem extends PureComponent {
|
||||
onPress={this.completeSuggestion}
|
||||
style={style.row}
|
||||
>
|
||||
<Text style={style.suggestionName}>{`/${trigger} ${hint}`}</Text>
|
||||
<Text style={style.suggestionName}>{`/${displayName || trigger} ${hint}`}</Text>
|
||||
<Text style={style.suggestionDescription}>{description}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -1,35 +1,46 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
BackHandler,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import DrawerLayout from 'react-native-drawer-layout';
|
||||
|
||||
import {General, WebsocketEvents} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import Drawer from 'app/components/drawer';
|
||||
import SafeAreaView from 'app/components/safe_area_view';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
|
||||
import ChannelsList from './channels_list';
|
||||
import DrawerSwiper from './drawer_swipper';
|
||||
import TeamsList from './teams_list';
|
||||
|
||||
const {
|
||||
ANDROID_TOP_LANDSCAPE,
|
||||
ANDROID_TOP_PORTRAIT,
|
||||
IOS_TOP_LANDSCAPE,
|
||||
IOS_TOP_PORTRAIT,
|
||||
} = ViewTypes;
|
||||
const DRAWER_INITIAL_OFFSET = 40;
|
||||
const DRAWER_LANDSCAPE_OFFSET = 150;
|
||||
|
||||
export default class ChannelSidebar extends Component {
|
||||
export default class ChannelDrawer extends Component {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
getTeams: PropTypes.func.isRequired,
|
||||
handleSelectChannel: PropTypes.func.isRequired,
|
||||
markChannelAsViewed: PropTypes.func.isRequired,
|
||||
makeDirectChannel: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired,
|
||||
setChannelLoading: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
@@ -37,18 +48,16 @@ export default class ChannelSidebar extends Component {
|
||||
children: PropTypes.node,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
isTablet: PropTypes.bool.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
teamsCount: PropTypes.number.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
closeHandle = null;
|
||||
openHandle = null;
|
||||
swiperIndex = 1;
|
||||
|
||||
constructor(props) {
|
||||
@@ -58,12 +67,9 @@ export default class ChannelSidebar extends Component {
|
||||
if (props.isLandscape || props.isTablet) {
|
||||
openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
|
||||
}
|
||||
|
||||
this.state = {
|
||||
show: false,
|
||||
lockMode: 'unlocked',
|
||||
openDrawerOffset,
|
||||
drawerOpened: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,7 +98,7 @@ export default class ChannelSidebar extends Component {
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const {currentTeamId, deviceWidth, isLandscape, teamsCount} = this.props;
|
||||
const {currentTeamId, isLandscape, teamsCount} = this.props;
|
||||
const {openDrawerOffset} = this.state;
|
||||
|
||||
if (nextState.openDrawerOffset !== openDrawerOffset || nextState.show !== this.state.show) {
|
||||
@@ -100,8 +106,8 @@ export default class ChannelSidebar extends Component {
|
||||
}
|
||||
|
||||
return nextProps.currentTeamId !== currentTeamId ||
|
||||
nextProps.isLandscape !== isLandscape || nextProps.deviceWidth !== deviceWidth ||
|
||||
nextProps.teamsCount !== teamsCount || this.state.lockMode !== nextState.lockMode;
|
||||
nextProps.isLandscape !== isLandscape ||
|
||||
nextProps.teamsCount !== teamsCount;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -112,8 +118,8 @@ export default class ChannelSidebar extends Component {
|
||||
}
|
||||
|
||||
handleAndroidBack = () => {
|
||||
if (this.state.drawerOpened && this.refs.drawer) {
|
||||
this.refs.drawer.closeDrawer();
|
||||
if (this.refs.drawer && this.refs.drawer.isOpened()) {
|
||||
this.refs.drawer.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,8 +131,8 @@ export default class ChannelSidebar extends Component {
|
||||
};
|
||||
|
||||
closeChannelDrawer = () => {
|
||||
if (this.state.drawerOpened && this.refs.drawer) {
|
||||
this.refs.drawer.closeDrawer();
|
||||
if (this.refs.drawer && this.refs.drawer.isOpened()) {
|
||||
this.refs.drawer.close();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,17 +141,53 @@ export default class ChannelSidebar extends Component {
|
||||
};
|
||||
|
||||
handleDrawerClose = () => {
|
||||
this.setState({
|
||||
drawerOpened: false,
|
||||
});
|
||||
this.resetDrawer();
|
||||
Keyboard.dismiss();
|
||||
|
||||
if (this.closeHandle) {
|
||||
InteractionManager.clearInteractionHandle(this.closeHandle);
|
||||
this.closeHandle = null;
|
||||
}
|
||||
};
|
||||
|
||||
handleDrawerCloseStart = () => {
|
||||
if (!this.closeHandle) {
|
||||
this.closeHandle = InteractionManager.createInteractionHandle();
|
||||
}
|
||||
};
|
||||
|
||||
handleDrawerOpen = () => {
|
||||
this.setState({
|
||||
drawerOpened: true,
|
||||
});
|
||||
if (this.state.openDrawerOffset !== 0) {
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
|
||||
if (this.openHandle) {
|
||||
InteractionManager.clearInteractionHandle(this.openHandle);
|
||||
this.openHandle = null;
|
||||
}
|
||||
};
|
||||
|
||||
handleDrawerOpenStart = () => {
|
||||
if (!this.openHandle) {
|
||||
this.openHandle = InteractionManager.createInteractionHandle();
|
||||
}
|
||||
};
|
||||
|
||||
handleDrawerTween = (ratio) => {
|
||||
const opacity = (ratio / 2);
|
||||
|
||||
EventEmitter.emit('drawer_opacity', opacity);
|
||||
|
||||
return {
|
||||
mainOverlay: {
|
||||
backgroundColor: this.props.theme.centerChannelBg,
|
||||
elevation: 3,
|
||||
opacity,
|
||||
},
|
||||
drawerOverlay: {
|
||||
backgroundColor: ratio ? '#000' : '#FFF',
|
||||
opacity: ratio ? (1 - ratio) / 2 : 1,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
handleUpdateTitle = (channel) => {
|
||||
@@ -156,56 +198,52 @@ export default class ChannelSidebar extends Component {
|
||||
this.props.actions.setChannelDisplayName(channelName);
|
||||
};
|
||||
|
||||
openChannelSidebar = () => {
|
||||
openChannelDrawer = () => {
|
||||
this.props.blurPostTextBox();
|
||||
|
||||
if (this.refs.drawer) {
|
||||
this.refs.drawer.openDrawer();
|
||||
if (this.refs.drawer && !this.refs.drawer.isOpened()) {
|
||||
this.refs.drawer.open();
|
||||
}
|
||||
};
|
||||
|
||||
selectChannel = (channel, currentChannelId, closeDrawer = true) => {
|
||||
selectChannel = (channel, currentChannelId) => {
|
||||
const {
|
||||
actions,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
handleSelectChannel,
|
||||
markChannelAsRead,
|
||||
setChannelLoading,
|
||||
setChannelDisplayName,
|
||||
markChannelAsViewed,
|
||||
} = actions;
|
||||
|
||||
tracker.channelSwitch = Date.now();
|
||||
|
||||
if (closeDrawer) {
|
||||
this.closeChannelDrawer();
|
||||
this.closeChannelDrawer();
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
setChannelLoading(channel.id !== currentChannelId);
|
||||
}
|
||||
setChannelDisplayName(channel.display_name);
|
||||
|
||||
if (!channel) {
|
||||
const utils = require('app/utils/general');
|
||||
const {intl} = this.context;
|
||||
|
||||
const unableToJoinMessage = {
|
||||
id: 'mobile.open_unknown_channel.error',
|
||||
defaultMessage: "We couldn't join the channel. Please reset the cache and try again.",
|
||||
};
|
||||
const erroMessage = {};
|
||||
|
||||
utils.alertErrorWithFallback(intl, erroMessage, unableToJoinMessage);
|
||||
setChannelLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setChannelDisplayName(channel.display_name);
|
||||
EventEmitter.emit('switch_channel', channel, currentChannelId);
|
||||
handleSelectChannel(channel.id);
|
||||
requestAnimationFrame(() => {
|
||||
// mark the channel as viewed after all the frame has flushed
|
||||
markChannelAsRead(channel.id, currentChannelId);
|
||||
if (channel.id !== currentChannelId) {
|
||||
markChannelAsViewed(currentChannelId);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
joinChannel = (channel, currentChannelId) => {
|
||||
const {intl} = this.context;
|
||||
joinChannel = async (channel, currentChannelId) => {
|
||||
const {
|
||||
actions,
|
||||
currentTeamId,
|
||||
currentUserId,
|
||||
intl,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@@ -213,60 +251,51 @@ export default class ChannelSidebar extends Component {
|
||||
makeDirectChannel,
|
||||
} = actions;
|
||||
|
||||
this.closeChannelDrawer();
|
||||
actions.setChannelLoading(channel.id !== currentChannelId);
|
||||
const displayValue = {displayName: channel.display_name};
|
||||
const utils = require('app/utils/general');
|
||||
|
||||
setTimeout(async () => {
|
||||
const displayValue = {displayName: channel.display_name};
|
||||
const utils = require('app/utils/general');
|
||||
let result;
|
||||
if (channel.type === General.DM_CHANNEL) {
|
||||
result = await makeDirectChannel(channel.id);
|
||||
|
||||
let result;
|
||||
if (channel.type === General.DM_CHANNEL) {
|
||||
result = await makeDirectChannel(channel.id, false);
|
||||
|
||||
if (result.error) {
|
||||
const dmFailedMessage = {
|
||||
id: 'mobile.open_dm.error',
|
||||
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again.",
|
||||
};
|
||||
utils.alertErrorWithFallback(intl, result.error, dmFailedMessage, displayValue);
|
||||
}
|
||||
} else {
|
||||
result = await joinChannel(currentUserId, currentTeamId, channel.id);
|
||||
|
||||
if (result.error || !result.data || !result.data.channel) {
|
||||
const joinFailedMessage = {
|
||||
id: 'mobile.join_channel.error',
|
||||
defaultMessage: "We couldn't join the channel {displayName}. Please check your connection and try again.",
|
||||
};
|
||||
utils.alertErrorWithFallback(intl, result.error, joinFailedMessage, displayValue);
|
||||
}
|
||||
if (result.error) {
|
||||
const dmFailedMessage = {
|
||||
id: 'mobile.open_dm.error',
|
||||
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again.",
|
||||
};
|
||||
utils.alertErrorWithFallback(intl, result.error, dmFailedMessage, displayValue);
|
||||
}
|
||||
} else {
|
||||
result = await joinChannel(currentUserId, currentTeamId, channel.id);
|
||||
|
||||
if (result.error || (!result.data && !result.data.channel)) {
|
||||
actions.setChannelLoading(false);
|
||||
return;
|
||||
if (result.error) {
|
||||
const joinFailedMessage = {
|
||||
id: 'mobile.join_channel.error',
|
||||
defaultMessage: "We couldn't join the channel {displayName}. Please check your connection and try again.",
|
||||
};
|
||||
utils.alertErrorWithFallback(intl, result.error, joinFailedMessage, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this.selectChannel(result.data.channel || result.data, currentChannelId, false);
|
||||
});
|
||||
}, 200);
|
||||
if (result.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectChannel(result.data, currentChannelId);
|
||||
};
|
||||
|
||||
onPageSelected = (index) => {
|
||||
this.swiperIndex = index;
|
||||
if (this.swiperIndex === 0) {
|
||||
this.setState({lockMode: 'locked-open'});
|
||||
} else {
|
||||
this.setState({lockMode: 'unlocked'});
|
||||
}
|
||||
};
|
||||
|
||||
onSearchEnds = () => {
|
||||
//hack to update the drawer when the offset changes
|
||||
const {isLandscape, isTablet} = this.props;
|
||||
|
||||
if (this.refs.drawer) {
|
||||
this.refs.drawer._syncAfterUpdate = true; //eslint-disable-line no-underscore-dangle
|
||||
}
|
||||
|
||||
let openDrawerOffset = DRAWER_INITIAL_OFFSET;
|
||||
if (isLandscape || isTablet) {
|
||||
openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
|
||||
@@ -275,6 +304,10 @@ export default class ChannelSidebar extends Component {
|
||||
};
|
||||
|
||||
onSearchStart = () => {
|
||||
if (this.refs.drawer) {
|
||||
this.refs.drawer._syncAfterUpdate = true; //eslint-disable-line no-underscore-dangle
|
||||
}
|
||||
|
||||
this.setState({openDrawerOffset: 0});
|
||||
};
|
||||
|
||||
@@ -290,7 +323,7 @@ export default class ChannelSidebar extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
renderNavigationView = () => {
|
||||
renderContent = () => {
|
||||
const {
|
||||
navigator,
|
||||
teamsCount,
|
||||
@@ -345,7 +378,6 @@ export default class ChannelSidebar extends Component {
|
||||
onSearchStart={this.onSearchStart}
|
||||
onSearchEnds={this.onSearchEnds}
|
||||
theme={theme}
|
||||
drawerOpened={this.state.drawerOpened}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
@@ -361,7 +393,6 @@ export default class ChannelSidebar extends Component {
|
||||
onPageSelected={this.onPageSelected}
|
||||
openDrawerOffset={openDrawerOffset}
|
||||
showTeams={showTeams}
|
||||
drawerOpened={this.state.drawerOpened}
|
||||
>
|
||||
{lists}
|
||||
</DrawerSwiper>
|
||||
@@ -370,20 +401,53 @@ export default class ChannelSidebar extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {children, deviceWidth} = this.props;
|
||||
const {lockMode, openDrawerOffset} = this.state;
|
||||
const {children, isLandscape} = this.props;
|
||||
const {openDrawerOffset} = this.state;
|
||||
|
||||
const androidTop = isLandscape ? ANDROID_TOP_LANDSCAPE : ANDROID_TOP_PORTRAIT;
|
||||
const iosTop = isLandscape ? IOS_TOP_LANDSCAPE : IOS_TOP_PORTRAIT;
|
||||
|
||||
return (
|
||||
<DrawerLayout
|
||||
drawerLockMode={lockMode}
|
||||
<Drawer
|
||||
ref='drawer'
|
||||
renderNavigationView={this.renderNavigationView}
|
||||
onDrawerClose={this.handleDrawerClose}
|
||||
onDrawerOpen={this.handleDrawerOpen}
|
||||
drawerWidth={deviceWidth - openDrawerOffset}
|
||||
onOpenStart={this.handleDrawerOpenStart}
|
||||
onOpen={this.handleDrawerOpen}
|
||||
onClose={this.handleDrawerClose}
|
||||
onCloseStart={this.handleDrawerCloseStart}
|
||||
captureGestures='open'
|
||||
type={Platform.OS === 'ios' ? 'static' : 'displace'}
|
||||
acceptTap={true}
|
||||
acceptPanOnDrawer={false}
|
||||
disabled={false}
|
||||
content={this.renderContent()}
|
||||
tapToClose={true}
|
||||
openDrawerOffset={openDrawerOffset}
|
||||
onRequestClose={this.closeChannelDrawer}
|
||||
panOpenMask={0.2}
|
||||
panCloseMask={openDrawerOffset}
|
||||
panThreshold={0.25}
|
||||
acceptPan={true}
|
||||
negotiatePan={true}
|
||||
useInteractionManager={false}
|
||||
tweenDuration={100}
|
||||
tweenHandler={this.handleDrawerTween}
|
||||
elevation={-5}
|
||||
bottomPanOffset={Platform.OS === 'ios' ? ANDROID_TOP_LANDSCAPE : IOS_TOP_PORTRAIT}
|
||||
topPanOffset={Platform.OS === 'ios' ? iosTop : androidTop}
|
||||
styles={{
|
||||
main: {
|
||||
shadowColor: '#000000',
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 12,
|
||||
shadowOffset: {
|
||||
width: -4,
|
||||
height: 0,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DrawerLayout>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ ShallowWrapper {
|
||||
currentChannelId="current_channel_id"
|
||||
displayName="display_name"
|
||||
fake={false}
|
||||
isArchived={false}
|
||||
isChannelMuted={false}
|
||||
isMyUser={true}
|
||||
isUnread={true}
|
||||
@@ -82,7 +81,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -161,7 +159,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -232,7 +229,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -295,7 +291,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -356,7 +351,6 @@ ShallowWrapper {
|
||||
"props": Object {
|
||||
"channelId": "channel_id",
|
||||
"isActive": false,
|
||||
"isArchived": false,
|
||||
"isInfo": false,
|
||||
"isUnread": true,
|
||||
"membersCount": 1,
|
||||
@@ -457,7 +451,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -536,7 +529,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -607,7 +599,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -670,7 +661,6 @@ ShallowWrapper {
|
||||
<ChannelIcon
|
||||
channelId="channel_id"
|
||||
isActive={false}
|
||||
isArchived={false}
|
||||
isInfo={false}
|
||||
isUnread={true}
|
||||
membersCount={1}
|
||||
@@ -731,7 +721,6 @@ ShallowWrapper {
|
||||
"props": Object {
|
||||
"channelId": "channel_id",
|
||||
"isActive": false,
|
||||
"isArchived": false,
|
||||
"isInfo": false,
|
||||
"isUnread": true,
|
||||
"membersCount": 1,
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -38,7 +38,6 @@ export default class ChannelItem extends PureComponent {
|
||||
type: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
unreadMsgs: PropTypes.number.isRequired,
|
||||
isArchived: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -98,23 +97,12 @@ export default class ChannelItem extends PureComponent {
|
||||
teammateDeletedAt,
|
||||
theme,
|
||||
type,
|
||||
isArchived,
|
||||
} = this.props;
|
||||
|
||||
// Only ever show an archived channel if it's the currently viewed channel.
|
||||
// It should disappear as soon as one navigates to another channel.
|
||||
if (isArchived && (currentChannelId !== channelId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.showChannelAsUnread() && shouldHideChannel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.props.displayName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {intl} = this.context;
|
||||
|
||||
let channelDisplayName = displayName;
|
||||
@@ -173,7 +161,6 @@ export default class ChannelItem extends PureComponent {
|
||||
teammateDeletedAt={teammateDeletedAt}
|
||||
theme={theme}
|
||||
type={type}
|
||||
isArchived={isArchived}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -242,7 +229,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
color: theme.sidebarUnreadText,
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: theme.mentionBg,
|
||||
backgroundColor: theme.mentionBj,
|
||||
borderColor: theme.sidebarHeaderBg,
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {configure, shallow} from 'enzyme';
|
||||
@@ -34,7 +34,6 @@ describe('ChannelItem', () => {
|
||||
sidebarTextHoverBg: '#aaa',
|
||||
},
|
||||
unreadMsgs: 1,
|
||||
isArchived: false,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -10,10 +10,9 @@ import {
|
||||
getMyChannelMember,
|
||||
shouldHideDefaultChannel,
|
||||
} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTheme, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
|
||||
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import ChannelItem from './channel_item';
|
||||
|
||||
@@ -27,15 +26,12 @@ function makeMapStateToProps() {
|
||||
|
||||
let isMyUser = false;
|
||||
let teammateDeletedAt = 0;
|
||||
let displayName = channel.display_name;
|
||||
if (channel.type === General.DM_CHANNEL && channel.teammate_id) {
|
||||
isMyUser = channel.teammate_id === currentUserId;
|
||||
const teammate = getUser(state, channel.teammate_id);
|
||||
if (teammate && teammate.delete_at) {
|
||||
teammateDeletedAt = teammate.delete_at;
|
||||
}
|
||||
const teammateNameDisplay = getTeammateNameDisplaySetting(state);
|
||||
displayName = displayUsername(teammate, teammateNameDisplay, false);
|
||||
}
|
||||
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
@@ -61,9 +57,10 @@ function makeMapStateToProps() {
|
||||
if (member && member.notify_props) {
|
||||
showUnreadForMsgs = member.notify_props.mark_unread !== General.MENTION;
|
||||
}
|
||||
|
||||
return {
|
||||
currentChannelId,
|
||||
displayName,
|
||||
displayName: channel.display_name,
|
||||
fake: channel.fake,
|
||||
isChannelMuted: isChannelMuted(member),
|
||||
isMyUser,
|
||||
@@ -75,7 +72,6 @@ function makeMapStateToProps() {
|
||||
theme: getTheme(state),
|
||||
type: channel.type,
|
||||
unreadMsgs,
|
||||
isArchived: channel.delete_at > 0,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -29,7 +29,6 @@ export default class ChannelsList extends PureComponent {
|
||||
onSelectChannel: PropTypes.func.isRequired,
|
||||
onShowTeams: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
drawerOpened: PropTypes.bool,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@@ -49,12 +48,6 @@ export default class ChannelsList extends PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!nextProps.drawerOpened && this.props.drawerOpened) {
|
||||
this.cancelSearch();
|
||||
}
|
||||
}
|
||||
|
||||
onSelectChannel = (channel, currentChannelId) => {
|
||||
if (channel.fake) {
|
||||
this.props.onJoinChannel(channel, currentChannelId);
|
||||
@@ -251,7 +244,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
lineHeight: 18,
|
||||
},
|
||||
above: {
|
||||
backgroundColor: theme.mentionBg,
|
||||
backgroundColor: theme.mentionBj,
|
||||
top: 9,
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import React, {Component} from 'react';
|
||||
@@ -19,7 +19,7 @@ import {General} from 'mattermost-redux/constants';
|
||||
import {sortChannelsByDisplayName} from 'mattermost-redux/utils/channel_utils';
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import ChannelItem from 'app/components/sidebars/main/channels_list/channel_item';
|
||||
import ChannelItem from 'app/components/channel_drawer/channels_list/channel_item';
|
||||
import {ListTypes} from 'app/constants';
|
||||
|
||||
const VIEWABILITY_CONFIG = ListTypes.VISIBILITY_CONFIG_DEFAULTS;
|
||||
@@ -114,7 +114,7 @@ class FilteredList extends Component {
|
||||
channelId={channel.id}
|
||||
channel={channel}
|
||||
isSearchResult={true}
|
||||
isUnread={channel.isUnread}
|
||||
isUnread={false}
|
||||
mentions={0}
|
||||
onSelectChannel={this.onSelectChannel}
|
||||
/>
|
||||
@@ -173,11 +173,8 @@ class FilteredList extends Component {
|
||||
buildUnreadChannelsForSearch = (props, term) => {
|
||||
const {unreadChannels} = props.channels;
|
||||
|
||||
return this.filterChannels(unreadChannels, term).map((item) => {
|
||||
item.isUnread = true;
|
||||
return item;
|
||||
});
|
||||
};
|
||||
return this.filterChannels(unreadChannels, term);
|
||||
}
|
||||
|
||||
buildCurrentDMSForSearch = (props, term) => {
|
||||
const {channels, teammateNameDisplay, profiles, statuses, pastDirectMessages, groupChannelMemberDetails} = props;
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -16,13 +16,11 @@ import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme, getFavoritesPreferences} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
|
||||
import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import List from './list';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const config = getConfig(state);
|
||||
const license = getLicense(state);
|
||||
const {config, license} = state.entities.general;
|
||||
const roles = getCurrentUserId(state) ? getCurrentUserRoles(state) : '';
|
||||
const unreadChannelIds = getSortedUnreadChannelIds(state);
|
||||
const favoriteChannelIds = getSortedFavoriteChannelIds(state);
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -16,7 +16,7 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {debounce} from 'mattermost-redux/actions/helpers';
|
||||
|
||||
import ChannelItem from 'app/components/sidebars/main/channels_list/channel_item';
|
||||
import ChannelItem from 'app/components/channel_drawer/channels_list/channel_item';
|
||||
import {ListTypes} from 'app/constants';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
@@ -313,7 +313,7 @@ export default class List extends PureComponent {
|
||||
|
||||
emitUnreadIndicatorChange = debounce((showIndicator) => {
|
||||
if (showIndicator && !UnreadIndicator) {
|
||||
UnreadIndicator = require('app/components/sidebars/main/channels_list/unread_indicator').default;
|
||||
UnreadIndicator = require('app/components/channel_drawer/channels_list/unread_indicator').default;
|
||||
}
|
||||
this.setState({showIndicator});
|
||||
}, 100);
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -9,7 +9,7 @@ import {getCurrentTeam, getMyTeamsCount, getChannelDrawerBadgeCount} from 'matte
|
||||
import SwitchTeamsButton from './switch_teams_button';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const team = getCurrentTeam(state) || {};
|
||||
const team = getCurrentTeam(state);
|
||||
|
||||
return {
|
||||
currentTeamId: team.id,
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
@@ -116,7 +116,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
fontSize: 14,
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: theme.mentionBg,
|
||||
backgroundColor: theme.mentionBj,
|
||||
borderColor: theme.sidebarHeaderBg,
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -17,7 +17,6 @@ export default class DrawerSwiper extends Component {
|
||||
openDrawerOffset: PropTypes.number,
|
||||
showTeams: PropTypes.bool.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
drawerOpened: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -26,12 +25,9 @@ export default class DrawerSwiper extends Component {
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const {deviceWidth, openDrawerOffset, showTeams, theme} = this.props;
|
||||
const {deviceWidth, showTeams, theme} = this.props;
|
||||
return nextProps.deviceWidth !== deviceWidth ||
|
||||
nextProps.showTeams !== showTeams ||
|
||||
nextProps.openDrawerOffset !== openDrawerOffset ||
|
||||
nextProps.theme !== theme ||
|
||||
nextProps.drawerOpened !== this.props.drawerOpened;
|
||||
nextProps.showTeams !== showTeams || nextProps.theme !== theme;
|
||||
}
|
||||
|
||||
runOnLayout = (shouldRun = true) => {
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {joinChannel} from 'mattermost-redux/actions/channels';
|
||||
import {joinChannel, markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels';
|
||||
import {getTeams} from 'mattermost-redux/actions/teams';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeamId, getMyTeamsCount} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
|
||||
import {handleSelectChannel, setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
|
||||
import {makeDirectChannel} from 'app/actions/views/more_dms';
|
||||
import {isLandscape, isTablet, getDimensions} from 'app/selectors/device';
|
||||
import {isLandscape, isTablet} from 'app/selectors/device';
|
||||
|
||||
import MainSidebar from './main_sidebar.js';
|
||||
import ChannelDrawer from './channel_drawer.js';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
return {
|
||||
...getDimensions(state),
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUserId,
|
||||
isLandscape: isLandscape(state),
|
||||
@@ -33,12 +32,15 @@ function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
getTeams,
|
||||
handleSelectChannel,
|
||||
joinChannel,
|
||||
markChannelAsViewed,
|
||||
makeDirectChannel,
|
||||
markChannelAsRead,
|
||||
setChannelDisplayName,
|
||||
setChannelLoading,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(MainSidebar);
|
||||
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(ChannelDrawer);
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -143,7 +143,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
fontSize: 20,
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: theme.mentionBg,
|
||||
backgroundColor: theme.mentionBj,
|
||||
borderColor: theme.sidebarHeaderBg,
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -25,7 +25,6 @@ export default class ChannelIcon extends React.PureComponent {
|
||||
teammateDeletedAt: PropTypes.number,
|
||||
theme: PropTypes.object.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
isArchived: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -46,7 +45,6 @@ export default class ChannelIcon extends React.PureComponent {
|
||||
teammateDeletedAt,
|
||||
theme,
|
||||
type,
|
||||
isArchived,
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
@@ -78,14 +76,8 @@ export default class ChannelIcon extends React.PureComponent {
|
||||
}
|
||||
|
||||
let icon;
|
||||
if (isArchived) {
|
||||
icon = (
|
||||
<Icon
|
||||
name='archive'
|
||||
style={[style.icon, unreadIcon, activeIcon, {fontSize: size}]}
|
||||
/>
|
||||
);
|
||||
} else if (type === General.OPEN_CHANNEL) {
|
||||
|
||||
if (type === General.OPEN_CHANNEL) {
|
||||
icon = (
|
||||
<Icon
|
||||
name='globe'
|
||||
|
||||