forked from Ivasoft/mattermost-mobile
Compare commits
9 Commits
v1.7.1
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e08d69b10 | ||
|
|
a8a95a98e7 | ||
|
|
b4b23d4d46 | ||
|
|
99cff03193 | ||
|
|
b9864bf26f | ||
|
|
e73b9017fc | ||
|
|
f65820dd0c | ||
|
|
7564ac023c | ||
|
|
5ef83639e9 |
@@ -40,7 +40,7 @@
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": false }],
|
||||
"camelcase": [2, {"properties": "never"}],
|
||||
"class-methods-use-this": 0,
|
||||
"comma-dangle": [2, "always-multiline"],
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2, {"before": false, "after": true}],
|
||||
"comma-style": [2, "last"],
|
||||
"complexity": [1, 10],
|
||||
@@ -54,7 +54,7 @@
|
||||
"eqeqeq": [2, "smart"],
|
||||
"func-call-spacing": [2, "never"],
|
||||
"func-names": 2,
|
||||
"func-style": [2, "declaration", { "allowArrowFunctions": true }],
|
||||
"func-style": [2, "declaration"],
|
||||
"generator-star-spacing": [0, {"before": false, "after": true}],
|
||||
"global-require": 2,
|
||||
"guard-for-in": 2,
|
||||
|
||||
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,44 +1,5 @@
|
||||
# Mattermost Mobile Apps Changelog
|
||||
|
||||
## v1.6.1 Release
|
||||
- Release Date: February 13, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed an issue preventing the app from going to the correct channel when opened from a push notification
|
||||
- Fixed an issue on Android devices where the app could sometimes freeze on the launch screen
|
||||
- Fixed an issue on Samsung devices causing extra letters to be insterted when typing to filter user lists
|
||||
|
||||
## v1.6.0 Release
|
||||
- Release Date: February 6, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Android File Sharing
|
||||
- Share files and images from other applications as attached files in Mattermost
|
||||
|
||||
### Improvements
|
||||
- Added a right drawer to access settings, edit profile information, change online status and logout
|
||||
- Added support for opening a Direct Message channel with yourself
|
||||
|
||||
### Bugs
|
||||
- Fixed a number of issues causing crashes on Android devices
|
||||
- Fixed an issue with auto capitalization on Android keyboards
|
||||
- Fixed an issue where the GitLab SSO login button sometimes didn't appear
|
||||
- Fixed an issue with link previews not appearing on some accounts
|
||||
- Fixed an issue where logging out of the app didn't clear the notification badge on the homescreen icon
|
||||
- Fixed an issue where interactive message buttons would not wrap to a new line
|
||||
- Fixed an issue where the keyboard would sometimes overlap the text input box
|
||||
- Fixed an issue where the Direct Message channel wouldn't open from the profile page
|
||||
- Fixed an issue where posts would sometimes overlap
|
||||
- Fixed an issue where the app sometimes hangs on logout
|
||||
|
||||
## v1.5.3 Release
|
||||
- Release Date: February 1, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
- Fixed a login issue when connecting to servers running a Data Retention policy
|
||||
|
||||
## v1.5.2 Release
|
||||
- Release Date: January 12, 2018
|
||||
- Server Versions Supported: Server v4.0+ is required, Self-Signed SSL Certificates are not supported
|
||||
|
||||
34
Makefile
34
Makefile
@@ -5,14 +5,14 @@
|
||||
.PHONY: build-ios build-android unsigned-ios unsigned-android
|
||||
.PHONY: test help
|
||||
|
||||
POD := $(shell which pod 2> /dev/null)
|
||||
POD := $(shell command -v pod 2> /dev/null)
|
||||
OS := $(shell sh -c 'uname -s 2>/dev/null')
|
||||
BASE_ASSETS = $(shell find assets/base -type d) $(shell find assets/base -type f -name '*')
|
||||
OVERRIDE_ASSETS = $(shell find assets/override -type d 2> /dev/null) $(shell find assets/override -type f -name '*' 2> /dev/null)
|
||||
|
||||
.yarninstall: package.json
|
||||
@if ! [ $(shell which yarn 2> /dev/null) ]; then \
|
||||
echo "yarn is not installed https://yarnpkg.com"; \
|
||||
@if ! [ $(shell command -v yarn 2> /dev/null) ]; then \
|
||||
@echo "yarn is not installed https://yarnpkg.com"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
@@ -47,7 +47,7 @@ pre-run: | .yarninstall .podinstall dist/assets ## Installs dependencies and ass
|
||||
|
||||
check-style: .yarninstall ## Runs eslint
|
||||
@echo Checking for style guide compliance
|
||||
@yarn run check
|
||||
@node_modules/.bin/eslint --ext \".js\" --ignore-pattern node_modules --quiet .
|
||||
|
||||
clean: ## Cleans dependencies, previous builds and temp files
|
||||
@echo Cleaning started
|
||||
@@ -75,11 +75,13 @@ post-install:
|
||||
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json
|
||||
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
|
||||
@sed -i'' -e 's|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_FULL_USER);|g' node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/params/Orientation.java
|
||||
@sed -i'' -e "s|var AndroidTextInput = requireNativeComponent('AndroidTextInput', null);|var AndroidTextInput = requireNativeComponent('CustomTextInput', null);|g" node_modules/react-native/Libraries/Components/TextInput/TextInput.js
|
||||
@sed -i'' -e "s|super.onBackPressed();|this.moveTaskToBack(true);|g" node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java
|
||||
@if [ $(shell grep "const Platform" node_modules/react-native/Libraries/Lists/VirtualizedList.js | grep -civ grep) -eq 0 ]; then \
|
||||
sed $ -i'' -e "s|const ReactNative = require('ReactNative');|const ReactNative = require('ReactNative');`echo $\\\\\\r;`const Platform = require('Platform');|g" node_modules/react-native/Libraries/Lists/VirtualizedList.js; \
|
||||
fi
|
||||
@sed -i'' -e 's|transform: \[{scaleY: -1}\],|...Platform.select({android: {transform: \[{perspective: 1}, {scaleY: -1}\]}, ios: {transform: \[{scaleY: -1}\]}}),|g' node_modules/react-native/Libraries/Lists/VirtualizedList.js
|
||||
@cd ./node_modules/react-native-svg/ios && rm -rf PerformanceBezier QuartzBookPack && yarn run postinstall
|
||||
@cd ./node_modules/mattermost-redux && yarn run build
|
||||
|
||||
start: | pre-run ## Starts the React Native packager server
|
||||
@@ -93,39 +95,39 @@ start: | pre-run ## Starts the React Native packager server
|
||||
|
||||
stop: ## Stops the React Native packager server
|
||||
@echo Stopping React Native packager server
|
||||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 1 ]; then \
|
||||
ps -ef | grep -i "cli.js start" | grep -iv grep | awk '{print $$2}' | xargs kill -9; \
|
||||
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 1 ]; then \
|
||||
ps -e | grep -i "cli.js start" | grep -iv grep | awk '{print $$1}' | xargs kill -9; \
|
||||
echo React Native packager server stopped; \
|
||||
else \
|
||||
echo No React Native packager server running; \
|
||||
fi
|
||||
|
||||
check-device-ios:
|
||||
@if ! [ $(shell which xcodebuild) ]; then \
|
||||
@if ! [ $(shell command -v xcodebuild) ]; then \
|
||||
echo "xcode is not installed"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! [ $(shell which watchman) ]; then \
|
||||
@if ! [ $(shell command -v watchman) ]; then \
|
||||
echo "watchman is not installed"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
check-device-android:
|
||||
@if ! [ $(ANDROID_HOME) ]; then \
|
||||
echo "ANDROID_HOME is not set"; \
|
||||
exit 1; \
|
||||
@echo "ANDROID_HOME is not set"; \
|
||||
@exit 1; \
|
||||
fi
|
||||
@if ! [ $(shell which adb 2> /dev/null) ]; then \
|
||||
echo "adb is not installed"; \
|
||||
exit 1; \
|
||||
@if ! [ $(shell command -v adb 2> /dev/null) ]; then \
|
||||
@echo "adb is not installed"; \
|
||||
@exit 1; \
|
||||
fi
|
||||
|
||||
@echo "Connect your Android device or open the emulator"
|
||||
@adb wait-for-device
|
||||
|
||||
@if ! [ $(shell which watchman 2> /dev/null) ]; then \
|
||||
echo "watchman is not installed"; \
|
||||
exit 1; \
|
||||
@if ! [ $(shell command -v watchman 2> /dev/null) ]; then \
|
||||
@echo "watchman is not installed"; \
|
||||
@exit 1; \
|
||||
fi
|
||||
|
||||
prepare-android-build:
|
||||
|
||||
35
NOTICE.txt
35
NOTICE.txt
@@ -2623,38 +2623,3 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-tableview
|
||||
|
||||
Native iOS UITableView for React Native with JSON support.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/aksonov/react-native-tableview
|
||||
|
||||
* LICENSE:
|
||||
|
||||
Copyright (c) 2015, aksonov
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
@@ -80,11 +80,6 @@ apply from: "../../node_modules/react-native/react.gradle"
|
||||
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||
|
||||
if (System.getenv("SENTRY_ENABLED") == "true") {
|
||||
project.ext.sentryCli = [
|
||||
logLevel: "debug",
|
||||
flavorAware: true
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native-sentry/sentry.gradle"
|
||||
}
|
||||
|
||||
@@ -111,8 +106,8 @@ android {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 23
|
||||
versionCode 92
|
||||
versionName "1.7.1"
|
||||
versionCode 86
|
||||
versionName "1.6.1"
|
||||
multiDexEnabled true
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.mattermost.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputType;
|
||||
|
||||
import com.facebook.react.views.textinput.ReactEditText;
|
||||
|
||||
public class CustomTextInput extends ReactEditText {
|
||||
private boolean autoScroll = false;
|
||||
|
||||
public CustomTextInput(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
private boolean isMultiline() {
|
||||
return (getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLayoutRequested() {
|
||||
if (isMultiline() && !autoScroll) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAutoScroll(boolean autoScroll) {
|
||||
this.autoScroll = autoScroll;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.mattermost.components;
|
||||
|
||||
import android.text.InputType;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import com.facebook.react.views.textinput.ReactTextInputManager;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewDefaults;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
public class CustomTextInputManager extends ReactTextInputManager {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "CustomTextInput";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomTextInput createViewInstance(ThemedReactContext context) {
|
||||
CustomTextInput editText = new CustomTextInput(context);
|
||||
int inputType = editText.getInputType();
|
||||
editText.setInputType(inputType & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
|
||||
editText.setReturnKeyType("done");
|
||||
editText.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_PX,
|
||||
(int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)));
|
||||
return editText;
|
||||
}
|
||||
|
||||
@ReactProp(name = "autoScroll", defaultBoolean = false)
|
||||
public void setAutoScroll(CustomTextInput view, boolean autoScroll) {
|
||||
view.setAutoScroll(autoScroll);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
|
||||
import com.mattermost.components.CustomTextInputManager;
|
||||
|
||||
public class MattermostPackage implements ReactPackage {
|
||||
private final MainApplication mApplication;
|
||||
|
||||
@@ -27,6 +29,8 @@ public class MattermostPackage implements ReactPackage {
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Arrays.<ViewManager>asList();
|
||||
return Arrays.<ViewManager>asList(
|
||||
new CustomTextInputManager()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ export function calculateDeviceDimensions() {
|
||||
type: DeviceTypes.DEVICE_DIMENSIONS_CHANGED,
|
||||
data: {
|
||||
deviceHeight: height,
|
||||
deviceWidth: width,
|
||||
},
|
||||
deviceWidth: width
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function connection(isOnline) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: DeviceTypes.CONNECTION_CHANGED,
|
||||
data: isOnline,
|
||||
data: isOnline
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
@@ -28,21 +28,21 @@ export function connection(isOnline) {
|
||||
export function setStatusBarHeight(height = 20) {
|
||||
return {
|
||||
type: DeviceTypes.STATUSBAR_HEIGHT_CHANGED,
|
||||
data: height,
|
||||
data: height
|
||||
};
|
||||
}
|
||||
|
||||
export function setDeviceOrientation(orientation) {
|
||||
return {
|
||||
type: DeviceTypes.DEVICE_ORIENTATION_CHANGED,
|
||||
data: orientation,
|
||||
data: orientation
|
||||
};
|
||||
}
|
||||
|
||||
export function setDeviceAsTablet() {
|
||||
return {
|
||||
type: DeviceTypes.DEVICE_TYPE_CHANGED,
|
||||
data: true,
|
||||
data: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,5 +51,5 @@ export default {
|
||||
connection,
|
||||
setDeviceOrientation,
|
||||
setDeviceAsTablet,
|
||||
setStatusBarHeight,
|
||||
setStatusBarHeight
|
||||
};
|
||||
|
||||
@@ -11,15 +11,15 @@ export function handleUpdateUserNotifyProps(notifyProps) {
|
||||
const config = state.entities.general.config;
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
const {interval, user_id: userId, ...otherProps} = notifyProps;
|
||||
const {interval, user_id, ...otherProps} = notifyProps;
|
||||
|
||||
const email = notifyProps.email;
|
||||
if (config.EnableEmailBatching === 'true' && email !== 'false') {
|
||||
const emailInterval = [{
|
||||
user_id: userId,
|
||||
user_id,
|
||||
category: Preferences.CATEGORY_NOTIFICATIONS,
|
||||
name: Preferences.EMAIL_INTERVAL,
|
||||
value: interval,
|
||||
value: interval
|
||||
}];
|
||||
|
||||
savePreferences(currentUserId, emailInterval)(dispatch, getState);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export function dismissBanner(text) {
|
||||
return {
|
||||
type: ViewTypes.ANNOUNCEMENT_BANNER,
|
||||
data: text,
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
fetchMyChannelsAndMembers,
|
||||
markChannelAsRead,
|
||||
selectChannel,
|
||||
leaveChannel as serviceLeaveChannel,
|
||||
leaveChannel as serviceLeaveChannel
|
||||
} from 'mattermost-redux/actions/channels';
|
||||
import {getPosts, getPostsBefore, getPostsSince, getPostThread} from 'mattermost-redux/actions/posts';
|
||||
import {getFilesForPost} from 'mattermost-redux/actions/files';
|
||||
@@ -19,14 +19,13 @@ import {getTeamMembersByIds} from 'mattermost-redux/actions/teams';
|
||||
import {getProfilesInChannel} from 'mattermost-redux/actions/users';
|
||||
import {General, Preferences} from 'mattermost-redux/constants';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTeamByName} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {
|
||||
getChannelByName,
|
||||
getDirectChannelName,
|
||||
getUserIdFromChannelName,
|
||||
isDirectChannel,
|
||||
isGroupChannel,
|
||||
isGroupChannel
|
||||
} from 'mattermost-redux/utils/channel_utils';
|
||||
import {getLastCreateAt} from 'mattermost-redux/utils/post_utils';
|
||||
import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils';
|
||||
@@ -41,18 +40,6 @@ export function loadChannelsIfNecessary(teamId) {
|
||||
};
|
||||
}
|
||||
|
||||
export function loadChannelsByTeamName(teamName) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {currentTeamId} = state.entities.teams;
|
||||
const team = getTeamByName(state, teamName);
|
||||
|
||||
if (team && team.id !== currentTeamId) {
|
||||
await dispatch(fetchMyChannelsAndMembers(team.id));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
@@ -71,7 +58,7 @@ export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
||||
user_id: currentUserId,
|
||||
category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
name,
|
||||
value: 'true',
|
||||
value: 'true'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,7 +130,7 @@ export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
||||
actions.push({
|
||||
type: UserTypes.RECEIVED_PROFILE_IN_CHANNEL,
|
||||
data: {user_id: members[i]},
|
||||
id: channel.id,
|
||||
id: channel.id
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -162,15 +149,10 @@ export function loadPostsIfNecessaryWithRetry(channelId) {
|
||||
|
||||
const time = Date.now();
|
||||
|
||||
let loadMorePostsVisible = true;
|
||||
let received;
|
||||
if (!postsIds || postsIds.length < ViewTypes.POST_VISIBILITY_CHUNK_SIZE) {
|
||||
// Get the first page of posts if it appears we haven't gotten it yet, like the webapp
|
||||
received = await retryGetPostsAction(getPosts(channelId), dispatch, getState);
|
||||
|
||||
if (received) {
|
||||
loadMorePostsVisible = received.order.length >= ViewTypes.POST_VISIBILITY_CHUNK_SIZE;
|
||||
}
|
||||
} else {
|
||||
const {lastConnectAt} = state.device.websocket;
|
||||
const lastGetPosts = state.views.channel.lastGetPosts[channelId];
|
||||
@@ -187,21 +169,15 @@ export function loadPostsIfNecessaryWithRetry(channelId) {
|
||||
}
|
||||
|
||||
received = await retryGetPostsAction(getPostsSince(channelId, since), dispatch, getState);
|
||||
|
||||
if (received) {
|
||||
loadMorePostsVisible = postsIds.length + received.order.length >= ViewTypes.POST_VISIBILITY_CHUNK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (received) {
|
||||
dispatch({
|
||||
type: ViewTypes.RECEIVED_POSTS_FOR_CHANNEL_AT_TIME,
|
||||
channelId,
|
||||
time,
|
||||
time
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(setLoadMorePostsVisible(loadMorePostsVisible));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -290,23 +266,20 @@ export function handleSelectChannel(channelId) {
|
||||
return async (dispatch, getState) => {
|
||||
const {currentTeamId} = getState().entities.teams;
|
||||
|
||||
dispatch(setLoadMorePostsVisible(true));
|
||||
|
||||
loadPostsIfNecessaryWithRetry(channelId)(dispatch, getState);
|
||||
selectChannel(channelId)(dispatch, getState);
|
||||
|
||||
dispatch(batchActions([
|
||||
{
|
||||
type: ViewTypes.SET_INITIAL_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
data: channelId
|
||||
},
|
||||
setChannelLoading(false),
|
||||
{
|
||||
type: ViewTypes.SET_LAST_CHANNEL_FOR_TEAM,
|
||||
teamId: currentTeamId,
|
||||
channelId,
|
||||
},
|
||||
]));
|
||||
channelId
|
||||
}
|
||||
]), 'BATCH_CHANNEL_LOADED');
|
||||
};
|
||||
}
|
||||
|
||||
@@ -315,7 +288,7 @@ export function handlePostDraftChanged(channelId, draft) {
|
||||
dispatch({
|
||||
type: ViewTypes.POST_DRAFT_CHANGED,
|
||||
channelId,
|
||||
draft,
|
||||
draft
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
@@ -324,7 +297,7 @@ export function handlePostDraftSelectionChanged(channelId, cursorPosition) {
|
||||
return {
|
||||
type: ViewTypes.POST_DRAFT_SELECTION_CHANGED,
|
||||
channelId,
|
||||
cursorPosition,
|
||||
cursorPosition
|
||||
};
|
||||
}
|
||||
|
||||
@@ -343,7 +316,7 @@ export function insertToDraft(value) {
|
||||
cursorPosition = threadDraft.cursorPosition;
|
||||
action = {
|
||||
type: ViewTypes.COMMENT_DRAFT_CHANGED,
|
||||
rootId: threadId,
|
||||
rootId: threadId
|
||||
};
|
||||
} else if (state.views.channel.drafts[channelId]) {
|
||||
const channelDraft = state.views.channel.drafts[channelId];
|
||||
@@ -351,7 +324,7 @@ export function insertToDraft(value) {
|
||||
cursorPosition = channelDraft.cursorPosition;
|
||||
action = {
|
||||
type: ViewTypes.POST_DRAFT_CHANGED,
|
||||
channelId,
|
||||
channelId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -365,7 +338,7 @@ export function insertToDraft(value) {
|
||||
if (action && nextDraft !== draft) {
|
||||
dispatch({
|
||||
...action,
|
||||
draft: nextDraft,
|
||||
draft: nextDraft
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -380,7 +353,7 @@ export function toggleDMChannel(otherUserId, visible) {
|
||||
user_id: currentUserId,
|
||||
category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
name: otherUserId,
|
||||
value: visible,
|
||||
value: visible
|
||||
}];
|
||||
|
||||
savePreferences(currentUserId, dm)(dispatch, getState);
|
||||
@@ -396,7 +369,7 @@ export function toggleGMChannel(channelId, visible) {
|
||||
user_id: currentUserId,
|
||||
category: Preferences.CATEGORY_GROUP_CHANNEL_SHOW,
|
||||
name: channelId,
|
||||
value: visible,
|
||||
value: visible
|
||||
}];
|
||||
|
||||
savePreferences(currentUserId, gm)(dispatch, getState);
|
||||
@@ -449,28 +422,28 @@ export function leaveChannel(channel, reset = false) {
|
||||
export function setChannelLoading(loading = true) {
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_LOADER,
|
||||
loading,
|
||||
loading
|
||||
};
|
||||
}
|
||||
|
||||
export function setChannelRefreshing(loading = true) {
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_REFRESHING,
|
||||
loading,
|
||||
loading
|
||||
};
|
||||
}
|
||||
|
||||
export function setChannelRetryFailed(failed = true) {
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_RETRY_FAILED,
|
||||
failed,
|
||||
failed
|
||||
};
|
||||
}
|
||||
|
||||
export function setChannelDisplayName(displayName) {
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_DISPLAY_NAME,
|
||||
displayName,
|
||||
displayName
|
||||
};
|
||||
}
|
||||
|
||||
@@ -493,10 +466,11 @@ export function increasePostVisibility(channelId, focusedPostId) {
|
||||
|
||||
if (loadedPostCount >= desiredPostVisibility) {
|
||||
// We already have the posts, so we just need to show them
|
||||
dispatch(batchActions([
|
||||
doIncreasePostVisibility(channelId),
|
||||
setLoadMorePostsVisible(true),
|
||||
]));
|
||||
dispatch({
|
||||
type: ViewTypes.INCREASE_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
amount: ViewTypes.POST_VISIBILITY_CHUNK_SIZE
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -505,49 +479,33 @@ export function increasePostVisibility(channelId, focusedPostId) {
|
||||
dispatch({
|
||||
type: ViewTypes.LOADING_POSTS,
|
||||
data: true,
|
||||
channelId,
|
||||
channelId
|
||||
});
|
||||
|
||||
const pageSize = ViewTypes.POST_VISIBILITY_CHUNK_SIZE;
|
||||
const page = Math.floor(currentPostVisibility / pageSize);
|
||||
const page = Math.floor(currentPostVisibility / ViewTypes.POST_VISIBILITY_CHUNK_SIZE);
|
||||
|
||||
let result;
|
||||
if (focusedPostId) {
|
||||
result = await getPostsBefore(channelId, focusedPostId, page, pageSize)(dispatch, getState);
|
||||
result = await getPostsBefore(channelId, focusedPostId, page, ViewTypes.POST_VISIBILITY_CHUNK_SIZE)(dispatch, getState);
|
||||
} else {
|
||||
result = await getPosts(channelId, page, pageSize)(dispatch, getState);
|
||||
result = await getPosts(channelId, page, ViewTypes.POST_VISIBILITY_CHUNK_SIZE)(dispatch, getState);
|
||||
}
|
||||
|
||||
const actions = [{
|
||||
type: ViewTypes.LOADING_POSTS,
|
||||
data: false,
|
||||
channelId,
|
||||
}];
|
||||
|
||||
const posts = result.data;
|
||||
if (posts) {
|
||||
// make sure to increment the posts visibility
|
||||
// only if we got results
|
||||
actions.push(doIncreasePostVisibility(channelId));
|
||||
|
||||
actions.push(setLoadMorePostsVisible(posts.order.length >= pageSize));
|
||||
dispatch({
|
||||
type: ViewTypes.INCREASE_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
amount: ViewTypes.POST_VISIBILITY_CHUNK_SIZE
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions));
|
||||
};
|
||||
}
|
||||
|
||||
function doIncreasePostVisibility(channelId) {
|
||||
return {
|
||||
type: ViewTypes.INCREASE_POST_VISIBILITY,
|
||||
data: channelId,
|
||||
amount: ViewTypes.POST_VISIBILITY_CHUNK_SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
function setLoadMorePostsVisible(visible) {
|
||||
return {
|
||||
type: ViewTypes.SET_LOAD_MORE_POSTS_VISIBLE,
|
||||
data: visible,
|
||||
dispatch({
|
||||
type: ViewTypes.LOADING_POSTS,
|
||||
data: false,
|
||||
channelId
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@ import {ViewTypes} from 'app/constants';
|
||||
|
||||
export function setLastUpgradeCheck() {
|
||||
return {
|
||||
type: ViewTypes.SET_LAST_UPGRADE_CHECK,
|
||||
type: ViewTypes.SET_LAST_UPGRADE_CHECK
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function executeCommand(message, channelId, rootId) {
|
||||
channel_id: channelId,
|
||||
team_id: teamId,
|
||||
root_id: rootId,
|
||||
parent_id: rootId,
|
||||
parent_id: rootId
|
||||
};
|
||||
|
||||
let msg = message;
|
||||
|
||||
@@ -18,7 +18,7 @@ export function handleCreateChannel(displayName, purpose, header, type) {
|
||||
display_name: displayName,
|
||||
purpose,
|
||||
header,
|
||||
type,
|
||||
type
|
||||
};
|
||||
|
||||
const {data} = await createChannel(channel, currentUserId)(dispatch, getState);
|
||||
|
||||
@@ -29,16 +29,6 @@ export function addReactionToLatestPost(emoji, rootId) {
|
||||
export function addRecentEmoji(emoji) {
|
||||
return {
|
||||
type: ViewTypes.ADD_RECENT_EMOJI,
|
||||
emoji,
|
||||
};
|
||||
}
|
||||
|
||||
export function incrementEmojiPickerPage() {
|
||||
return async (dispatch) => {
|
||||
dispatch({
|
||||
type: ViewTypes.INCREMENT_EMOJI_PICKER_PAGE,
|
||||
});
|
||||
|
||||
return {data: true};
|
||||
emoji
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ import {ViewTypes} from 'app/constants';
|
||||
export function addFileToFetchCache(url) {
|
||||
return {
|
||||
type: ViewTypes.ADD_FILE_TO_FETCH_CACHE,
|
||||
url,
|
||||
url
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import FormData from 'form-data';
|
||||
import {Platform} from 'react-native';
|
||||
import {uploadFile} from 'mattermost-redux/actions/files';
|
||||
import {parseClientIdsFromFormData} from 'mattermost-redux/utils/file_utils';
|
||||
|
||||
import {
|
||||
buildFileUploadData,
|
||||
encodeHeaderURIStringToUTF8,
|
||||
generateId,
|
||||
} from 'app/utils/file';
|
||||
import {buildFileUploadData, generateId} from 'app/utils/file';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export function handleUploadFiles(files, rootId) {
|
||||
@@ -28,10 +26,9 @@ export function handleUploadFiles(files, rootId) {
|
||||
localPath: fileData.uri,
|
||||
name: fileData.name,
|
||||
type: fileData.mimeType,
|
||||
extension: fileData.extension,
|
||||
extension: fileData.extension
|
||||
});
|
||||
|
||||
fileData.name = encodeHeaderURIStringToUTF8(fileData.name);
|
||||
formData.append('files', fileData);
|
||||
formData.append('channel_id', channelId);
|
||||
formData.append('client_ids', clientId);
|
||||
@@ -46,11 +43,10 @@ export function handleUploadFiles(files, rootId) {
|
||||
type: ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT,
|
||||
clientIds,
|
||||
channelId,
|
||||
rootId,
|
||||
rootId
|
||||
});
|
||||
|
||||
const clientIdsArray = clientIds.map((c) => c.clientId);
|
||||
await uploadFile(channelId, rootId, clientIdsArray, formData, formBoundary)(dispatch, getState);
|
||||
await uploadFile(channelId, rootId, parseClientIdsFromFormData(formData), formData, formBoundary)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -60,11 +56,13 @@ export function retryFileUpload(file, rootId) {
|
||||
|
||||
const channelId = state.entities.channels.currentChannelId;
|
||||
const formData = new FormData();
|
||||
const fileData = buildFileUploadData(file);
|
||||
|
||||
fileData.uri = file.localPath;
|
||||
const fileData = {
|
||||
uri: file.localPath,
|
||||
name: file.name,
|
||||
type: file.type
|
||||
};
|
||||
|
||||
fileData.name = encodeHeaderURIStringToUTF8(fileData.name);
|
||||
formData.append('files', fileData);
|
||||
formData.append('channel_id', channelId);
|
||||
formData.append('client_ids', file.clientId);
|
||||
@@ -78,7 +76,7 @@ export function retryFileUpload(file, rootId) {
|
||||
type: ViewTypes.RETRY_UPLOAD_FILE_FOR_POST,
|
||||
clientId: file.clientId,
|
||||
channelId,
|
||||
rootId,
|
||||
rootId
|
||||
});
|
||||
|
||||
await uploadFile(channelId, rootId, [file.clientId], formData, formBoundary)(dispatch, getState);
|
||||
@@ -89,7 +87,7 @@ export function handleClearFiles(channelId, rootId) {
|
||||
return {
|
||||
type: ViewTypes.CLEAR_FILES_FOR_POST_DRAFT,
|
||||
channelId,
|
||||
rootId,
|
||||
rootId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,7 +95,7 @@ export function handleClearFailedFiles(channelId, rootId) {
|
||||
return {
|
||||
type: ViewTypes.CLEAR_FAILED_FILES_FOR_POST_DRAFT,
|
||||
channelId,
|
||||
rootId,
|
||||
rootId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,7 +104,7 @@ export function handleRemoveFile(clientId, channelId, rootId) {
|
||||
type: ViewTypes.REMOVE_FILE_FROM_POST_DRAFT,
|
||||
clientId,
|
||||
channelId,
|
||||
rootId,
|
||||
rootId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -114,6 +112,6 @@ export function handleRemoveLastFile(channelId, rootId) {
|
||||
return {
|
||||
type: ViewTypes.REMOVE_LAST_FILE_FROM_POST_DRAFT,
|
||||
channelId,
|
||||
rootId,
|
||||
rootId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function handleLoginIdChanged(loginId) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ViewTypes.LOGIN_ID_CHANGED,
|
||||
loginId,
|
||||
loginId
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export function handlePasswordChanged(password) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ViewTypes.PASSWORD_CHANGED,
|
||||
password,
|
||||
password
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
@@ -35,8 +35,8 @@ export function handleSuccessfulLogin() {
|
||||
type: GeneralTypes.RECEIVED_APP_CREDENTIALS,
|
||||
data: {
|
||||
url,
|
||||
token,
|
||||
},
|
||||
token
|
||||
}
|
||||
}, getState);
|
||||
|
||||
if (config.DataRetentionEnableMessageDeletion && config.DataRetentionEnableMessageDeletion === 'true' &&
|
||||
@@ -73,5 +73,5 @@ export default {
|
||||
handleLoginIdChanged,
|
||||
handlePasswordChanged,
|
||||
handleSuccessfulLogin,
|
||||
getSession,
|
||||
getSession
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import {recordTime} from 'app/utils/segment';
|
||||
import {
|
||||
handleSelectChannel,
|
||||
setChannelDisplayName,
|
||||
retryGetPostsAction,
|
||||
retryGetPostsAction
|
||||
} from 'app/actions/views/channel';
|
||||
|
||||
export function loadConfigAndLicense() {
|
||||
@@ -22,7 +22,7 @@ export function loadConfigAndLicense() {
|
||||
const {currentUserId} = getState().entities.users;
|
||||
const [configData, licenseData] = await Promise.all([
|
||||
getClientConfig()(dispatch, getState),
|
||||
getLicenseConfig()(dispatch, getState),
|
||||
getLicenseConfig()(dispatch, getState)
|
||||
]);
|
||||
|
||||
const config = configData.data || {};
|
||||
@@ -56,7 +56,7 @@ export function loadFromPushNotification(notification) {
|
||||
if (teamId && (!teams[teamId] || !myTeamMembers[teamId])) {
|
||||
await Promise.all([
|
||||
getMyTeams()(dispatch, getState),
|
||||
getMyTeamMembers()(dispatch, getState),
|
||||
getMyTeamMembers()(dispatch, getState)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export function createPost(post) {
|
||||
...post,
|
||||
pending_post_id: pendingPostId,
|
||||
create_at: timestamp,
|
||||
update_at: timestamp,
|
||||
update_at: timestamp
|
||||
};
|
||||
|
||||
return Client4.createPost({...newPost, create_at: 0}).then((payload) => {
|
||||
@@ -104,10 +104,10 @@ export function createPost(post) {
|
||||
data: {
|
||||
order: [],
|
||||
posts: {
|
||||
[payload.id]: payload,
|
||||
},
|
||||
[payload.id]: payload
|
||||
}
|
||||
},
|
||||
channelId: payload.channel_id,
|
||||
channelId: payload.channel_id
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -124,5 +124,5 @@ export function recordLoadTime(screenName, category) {
|
||||
export default {
|
||||
loadConfigAndLicense,
|
||||
loadFromPushNotification,
|
||||
purgeOfflineStore,
|
||||
purgeOfflineStore
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ export function handleSearchDraftChanged(text) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ViewTypes.SEARCH_DRAFT_CHANGED,
|
||||
text,
|
||||
text
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ export function handleServerUrlChanged(serverUrl) {
|
||||
dispatch(batchActions([
|
||||
{type: GeneralTypes.CLIENT_CONFIG_RESET},
|
||||
{type: GeneralTypes.CLIENT_LICENSE_RESET},
|
||||
{type: ViewTypes.SERVER_URL_CHANGED, serverUrl},
|
||||
{type: ViewTypes.SERVER_URL_CHANGED, serverUrl}
|
||||
]), getState);
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
handleServerUrlChanged,
|
||||
handleServerUrlChanged
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ export function handleTeamChange(teamId, selectChannel = true) {
|
||||
|
||||
const actions = [
|
||||
setChannelDisplayName(''),
|
||||
{type: TeamTypes.SELECT_TEAM, data: teamId},
|
||||
{type: TeamTypes.SELECT_TEAM, data: teamId}
|
||||
];
|
||||
|
||||
if (selectChannel) {
|
||||
@@ -55,5 +55,5 @@ export function selectFirstAvailableTeam() {
|
||||
|
||||
export default {
|
||||
handleTeamChange,
|
||||
selectFirstAvailableTeam,
|
||||
selectFirstAvailableTeam
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ export function handleCommentDraftChanged(rootId, draft) {
|
||||
dispatch({
|
||||
type: ViewTypes.COMMENT_DRAFT_CHANGED,
|
||||
rootId,
|
||||
draft,
|
||||
draft
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
@@ -17,6 +17,6 @@ export function handleCommentDraftSelectionChanged(rootId, cursorPosition) {
|
||||
return {
|
||||
type: ViewTypes.COMMENT_DRAFT_SELECTION_CHANGED,
|
||||
rootId,
|
||||
cursorPosition,
|
||||
cursorPosition
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
// 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,
|
||||
TouchableOpacity,
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
};
|
||||
|
||||
state = {
|
||||
bannerHeight: new Animated.Value(0),
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const {bannerDismissed, bannerEnabled, bannerText} = this.props;
|
||||
const showBanner = bannerEnabled && !bannerDismissed && Boolean(bannerText);
|
||||
this.toggleBanner(showBanner);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.bannerText !== nextProps.bannerText ||
|
||||
this.props.bannerEnabled !== nextProps.bannerEnabled ||
|
||||
this.props.bannerDismissed !== nextProps.bannerDismissed) {
|
||||
const showBanner = nextProps.bannerEnabled && !nextProps.bannerDismissed && Boolean(nextProps.bannerText);
|
||||
this.toggleBanner(showBanner);
|
||||
}
|
||||
}
|
||||
|
||||
handleDismiss = () => {
|
||||
const {actions, bannerText} = this.props;
|
||||
actions.dismissBanner(bannerText);
|
||||
};
|
||||
|
||||
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) => {
|
||||
const value = show ? 38 : 0;
|
||||
Animated.timing(this.state.bannerHeight, {
|
||||
toValue: value,
|
||||
duration: 350,
|
||||
}).start();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {bannerHeight} = this.state;
|
||||
|
||||
const bannerStyle = {
|
||||
backgroundColor: this.props.bannerColor,
|
||||
height: bannerHeight,
|
||||
};
|
||||
|
||||
const bannerTextStyle = {
|
||||
color: this.props.bannerTextColor,
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatedView
|
||||
style={[style.bannerContainer, bannerStyle]}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={this.handlePress}
|
||||
style={style.wrapper}
|
||||
>
|
||||
<Text
|
||||
ellipsizeMode='tail'
|
||||
numberOfLines={1}
|
||||
style={[style.bannerText, bannerTextStyle]}
|
||||
>
|
||||
{this.props.bannerText}
|
||||
</Text>
|
||||
<MaterialIcons
|
||||
color={this.props.bannerTextColor}
|
||||
name='info'
|
||||
size={16}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</AnimatedView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const style = StyleSheet.create({
|
||||
bannerContainer: {
|
||||
paddingHorizontal: 10,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
},
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
bannerText: {
|
||||
flex: 1,
|
||||
fontSize: 14,
|
||||
marginRight: 5,
|
||||
},
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
// 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 {dismissBanner} from 'app/actions/views/announcement';
|
||||
|
||||
import AnnouncementBanner from './announcement_banner';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const config = getConfig(state);
|
||||
const license = getLicense(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,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
dismissBanner,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner);
|
||||
@@ -5,14 +5,14 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Svg, {
|
||||
G,
|
||||
Path,
|
||||
Path
|
||||
} from 'react-native-svg';
|
||||
|
||||
export default class AwayStatus extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Clipboard, Platform, Text} from 'react-native';
|
||||
import {Clipboard, Text} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
@@ -19,11 +19,11 @@ export default class AtMention extends React.PureComponent {
|
||||
onPostPress: PropTypes.func,
|
||||
textStyle: CustomPropTypes.Style,
|
||||
theme: PropTypes.object.isRequired,
|
||||
usersByUsername: PropTypes.object.isRequired,
|
||||
usersByUsername: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
intl: intlShape
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@@ -32,7 +32,7 @@ export default class AtMention extends React.PureComponent {
|
||||
const userDetails = this.getUserDetailsFromMentionName(props);
|
||||
this.state = {
|
||||
username: userDetails.username,
|
||||
id: userDetails.id,
|
||||
id: userDetails.id
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class AtMention extends React.PureComponent {
|
||||
const userDetails = this.getUserDetailsFromMentionName(nextProps);
|
||||
this.setState({
|
||||
username: userDetails.username,
|
||||
id: userDetails.id,
|
||||
id: userDetails.id
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -49,27 +49,22 @@ export default class AtMention extends React.PureComponent {
|
||||
goToUserProfile = () => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {intl} = this.context;
|
||||
const options = {
|
||||
|
||||
navigator.push({
|
||||
screen: 'UserProfile',
|
||||
title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
userId: this.state.id,
|
||||
userId: this.state.id
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
navigator.push(options);
|
||||
} else {
|
||||
navigator.showModal(options);
|
||||
}
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getUserDetailsFromMentionName(props) {
|
||||
@@ -80,7 +75,7 @@ export default class AtMention extends React.PureComponent {
|
||||
const user = props.usersByUsername[mentionName];
|
||||
return {
|
||||
username: user.username,
|
||||
id: user.id,
|
||||
id: user.id
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,7 +88,7 @@ export default class AtMention extends React.PureComponent {
|
||||
}
|
||||
|
||||
return {
|
||||
username: '',
|
||||
username: ''
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,9 +102,9 @@ export default class AtMention extends React.PureComponent {
|
||||
action = {
|
||||
text: intl.formatMessage({
|
||||
id: 'mobile.mention.copy_mention',
|
||||
defaultMessage: 'Copy Mention',
|
||||
defaultMessage: 'Copy Mention'
|
||||
}),
|
||||
onPress: this.handleCopyMention,
|
||||
onPress: this.handleCopyMention
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import AtMention from './at_mention';
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
usersByUsername: getUsersByUsername(state),
|
||||
usersByUsername: getUsersByUsername(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,13 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Alert,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import Permissions from 'react-native-permissions';
|
||||
|
||||
import {PermissionTypes} from 'app/constants';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
|
||||
class AttachmentButton extends PureComponent {
|
||||
@@ -25,50 +22,46 @@ class AttachmentButton extends PureComponent {
|
||||
onShowFileMaxWarning: PropTypes.func,
|
||||
theme: PropTypes.object.isRequired,
|
||||
uploadFiles: PropTypes.func.isRequired,
|
||||
wrapper: PropTypes.bool,
|
||||
wrapper: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
maxFileCount: 5,
|
||||
maxFileCount: 5
|
||||
};
|
||||
|
||||
attachFileFromCamera = async () => {
|
||||
attachFileFromCamera = () => {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const options = {
|
||||
quality: 1.0,
|
||||
noData: true,
|
||||
storageOptions: {
|
||||
cameraRoll: true,
|
||||
waitUntilSaved: true,
|
||||
waitUntilSaved: true
|
||||
},
|
||||
permissionDenied: {
|
||||
title: formatMessage({
|
||||
id: 'mobile.android.camera_permission_denied_title',
|
||||
defaultMessage: 'Camera access is required',
|
||||
defaultMessage: 'Camera access is required'
|
||||
}),
|
||||
text: formatMessage({
|
||||
id: 'mobile.android.camera_permission_denied_description',
|
||||
defaultMessage: 'To take photos and videos with your camera, please change your permission settings.',
|
||||
defaultMessage: 'To take photos and videos with your camera, please change your permission settings.'
|
||||
}),
|
||||
reTryTitle: formatMessage({
|
||||
id: 'mobile.android.permission_denied_retry',
|
||||
defaultMessage: 'Set Permission',
|
||||
defaultMessage: 'Set Permission'
|
||||
}),
|
||||
okTitle: formatMessage({id: 'mobile.android.permission_denied_dismiss', defaultMessage: 'Dismiss'}),
|
||||
},
|
||||
okTitle: formatMessage({id: 'mobile.android.permission_denied_dismiss', defaultMessage: 'Dismiss'})
|
||||
}
|
||||
};
|
||||
|
||||
const hasPhotoPermission = await this.hasPhotoPermission();
|
||||
ImagePicker.launchCamera(options, (response) => {
|
||||
if (response.error || response.didCancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPhotoPermission) {
|
||||
ImagePicker.launchCamera(options, (response) => {
|
||||
if (response.error || response.didCancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadFiles([response]);
|
||||
});
|
||||
}
|
||||
this.uploadFiles([response]);
|
||||
});
|
||||
};
|
||||
|
||||
attachFileFromLibrary = () => {
|
||||
@@ -79,18 +72,18 @@ class AttachmentButton extends PureComponent {
|
||||
permissionDenied: {
|
||||
title: formatMessage({
|
||||
id: 'mobile.android.photos_permission_denied_title',
|
||||
defaultMessage: 'Photo library access is required',
|
||||
defaultMessage: 'Photo library access is required'
|
||||
}),
|
||||
text: formatMessage({
|
||||
id: 'mobile.android.photos_permission_denied_description',
|
||||
defaultMessage: 'To upload images from your library, please change your permission settings.',
|
||||
defaultMessage: 'To upload images from your library, please change your permission settings.'
|
||||
}),
|
||||
reTryTitle: formatMessage({
|
||||
id: 'mobile.android.permission_denied_retry',
|
||||
defaultMessage: 'Set Permission',
|
||||
defaultMessage: 'Set Permission'
|
||||
}),
|
||||
okTitle: formatMessage({id: 'mobile.android.permission_denied_dismiss', defaultMessage: 'Dismiss'}),
|
||||
},
|
||||
okTitle: formatMessage({id: 'mobile.android.permission_denied_dismiss', defaultMessage: 'Dismiss'})
|
||||
}
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
@@ -115,18 +108,18 @@ class AttachmentButton extends PureComponent {
|
||||
permissionDenied: {
|
||||
title: formatMessage({
|
||||
id: 'mobile.android.videos_permission_denied_title',
|
||||
defaultMessage: 'Video library access is required',
|
||||
defaultMessage: 'Video library access is required'
|
||||
}),
|
||||
text: formatMessage({
|
||||
id: 'mobile.android.videos_permission_denied_description',
|
||||
defaultMessage: 'To upload videos from your library, please change your permission settings.',
|
||||
defaultMessage: 'To upload videos from your library, please change your permission settings.'
|
||||
}),
|
||||
reTryTitle: formatMessage({
|
||||
id: 'mobile.android.permission_denied_retry',
|
||||
defaultMessage: 'Set Permission',
|
||||
defaultMessage: 'Set Permission'
|
||||
}),
|
||||
okTitle: formatMessage({id: 'mobile.android.permission_denied_dismiss', defaultMessage: 'Dismiss'}),
|
||||
},
|
||||
okTitle: formatMessage({id: 'mobile.android.permission_denied_dismiss', defaultMessage: 'Dismiss'})
|
||||
}
|
||||
};
|
||||
|
||||
ImagePicker.launchImageLibrary(options, (response) => {
|
||||
@@ -138,66 +131,13 @@ class AttachmentButton extends PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
hasPhotoPermission = async () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
const {formatMessage} = this.props.intl;
|
||||
let permissionRequest;
|
||||
const hasPermissionToStorage = await Permissions.check('photo');
|
||||
|
||||
switch (hasPermissionToStorage) {
|
||||
case PermissionTypes.UNDETERMINED:
|
||||
permissionRequest = await Permissions.request('photo');
|
||||
if (permissionRequest !== PermissionTypes.AUTHORIZED) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PermissionTypes.DENIED: {
|
||||
const canOpenSettings = await Permissions.canOpenSettings();
|
||||
let grantOption = null;
|
||||
if (canOpenSettings) {
|
||||
grantOption = {
|
||||
text: formatMessage({
|
||||
id: 'mobile.android.permission_denied_retry',
|
||||
defaultMessage: 'Set permission',
|
||||
}),
|
||||
onPress: () => Permissions.openSettings(),
|
||||
};
|
||||
}
|
||||
|
||||
Alert.alert(
|
||||
formatMessage({
|
||||
id: 'mobile.android.photos_permission_denied_title',
|
||||
defaultMessage: 'Photo library access is required',
|
||||
}),
|
||||
formatMessage({
|
||||
id: 'mobile.android.photos_permission_denied_description',
|
||||
defaultMessage: 'To upload images from your library, please change your permission settings.',
|
||||
}),
|
||||
[
|
||||
grantOption,
|
||||
{
|
||||
text: formatMessage({
|
||||
id: 'mobile.android.permission_denied_dismiss',
|
||||
defaultMessage: 'Dismiss',
|
||||
}),
|
||||
},
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
uploadFiles = (images) => {
|
||||
this.props.uploadFiles(images);
|
||||
};
|
||||
|
||||
handleFileAttachmentOption = (action) => {
|
||||
this.props.navigator.dismissModal({
|
||||
animationType: 'none',
|
||||
animationType: 'none'
|
||||
});
|
||||
|
||||
// Have to wait to launch the library attachment action.
|
||||
@@ -224,17 +164,17 @@ class AttachmentButton extends PureComponent {
|
||||
action: () => this.handleFileAttachmentOption(this.attachFileFromCamera),
|
||||
text: {
|
||||
id: 'mobile.file_upload.camera',
|
||||
defaultMessage: 'Take Photo or Video',
|
||||
defaultMessage: 'Take Photo or Video'
|
||||
},
|
||||
icon: 'camera',
|
||||
icon: 'camera'
|
||||
}, {
|
||||
action: () => this.handleFileAttachmentOption(this.attachFileFromLibrary),
|
||||
text: {
|
||||
id: 'mobile.file_upload.library',
|
||||
defaultMessage: 'Photo Library',
|
||||
defaultMessage: 'Photo Library'
|
||||
},
|
||||
icon: 'photo',
|
||||
}],
|
||||
icon: 'photo'
|
||||
}]
|
||||
};
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
@@ -242,9 +182,9 @@ class AttachmentButton extends PureComponent {
|
||||
action: () => this.handleFileAttachmentOption(this.attachVideoFromLibraryAndroid),
|
||||
text: {
|
||||
id: 'mobile.file_upload.video',
|
||||
defaultMessage: 'Video Library',
|
||||
defaultMessage: 'Video Library'
|
||||
},
|
||||
icon: 'file-video-o',
|
||||
icon: 'file-video-o'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -253,15 +193,15 @@ class AttachmentButton extends PureComponent {
|
||||
title: '',
|
||||
animationType: 'none',
|
||||
passProps: {
|
||||
items: options.items,
|
||||
items: options.items
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
modalPresentationStyle: 'overCurrentContext'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -298,18 +238,18 @@ const style = StyleSheet.create({
|
||||
attachIcon: {
|
||||
marginTop: Platform.select({
|
||||
ios: 2,
|
||||
android: 0,
|
||||
}),
|
||||
android: 0
|
||||
})
|
||||
},
|
||||
buttonContainer: {
|
||||
height: Platform.select({
|
||||
ios: 34,
|
||||
android: 36,
|
||||
android: 36
|
||||
}),
|
||||
width: 45,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
justifyContent: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
export default injectIntl(AttachmentButton);
|
||||
|
||||
@@ -17,7 +17,7 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
export default class AtMention extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
autocompleteUsers: PropTypes.func.isRequired,
|
||||
autocompleteUsers: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
currentChannelId: PropTypes.string,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
@@ -33,20 +33,20 @@ export default class AtMention extends PureComponent {
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
teamMembers: PropTypes.array,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
defaultChannel: {},
|
||||
isSearch: false,
|
||||
value: '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sections: [],
|
||||
sections: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export default class AtMention extends PureComponent {
|
||||
// if the term changes but is null or the mention has been completed we render this component as null
|
||||
this.setState({
|
||||
mentionComplete: false,
|
||||
sections: [],
|
||||
sections: []
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(0);
|
||||
@@ -84,7 +84,7 @@ export default class AtMention extends PureComponent {
|
||||
id: 'mobile.suggestion.members',
|
||||
defaultMessage: 'Members',
|
||||
data: teamMembers,
|
||||
key: 'teamMembers',
|
||||
key: 'teamMembers'
|
||||
});
|
||||
} else {
|
||||
if (inChannel.length) {
|
||||
@@ -92,7 +92,7 @@ export default class AtMention extends PureComponent {
|
||||
id: 'suggestion.mention.members',
|
||||
defaultMessage: 'Channel Members',
|
||||
data: inChannel,
|
||||
key: 'inChannel',
|
||||
key: 'inChannel'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export default class AtMention extends PureComponent {
|
||||
defaultMessage: 'Special Mentions',
|
||||
data: this.getSpecialMentions(),
|
||||
key: 'special',
|
||||
renderItem: this.renderSpecialMentions,
|
||||
renderItem: this.renderSpecialMentions
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,13 +111,13 @@ export default class AtMention extends PureComponent {
|
||||
id: 'suggestion.mention.nonmembers',
|
||||
defaultMessage: 'Not in Channel',
|
||||
data: outChannel,
|
||||
key: 'outChannel',
|
||||
key: 'outChannel'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
sections,
|
||||
sections
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(sections.reduce((total, section) => total + section.data.length, 0));
|
||||
@@ -134,16 +134,16 @@ export default class AtMention extends PureComponent {
|
||||
id: 'suggestion.mention.all',
|
||||
defaultMessage: 'Notifies everyone in the channel, use in {townsquare} to notify the whole team',
|
||||
values: {
|
||||
townsquare: this.props.defaultChannel.display_name,
|
||||
},
|
||||
townsquare: this.props.defaultChannel.display_name
|
||||
}
|
||||
}, {
|
||||
completeHandle: 'channel',
|
||||
id: 'suggestion.mention.channel',
|
||||
defaultMessage: 'Notifies everyone in the channel',
|
||||
defaultMessage: 'Notifies everyone in the channel'
|
||||
}, {
|
||||
completeHandle: 'here',
|
||||
id: 'suggestion.mention.here',
|
||||
defaultMessage: 'Notifies everyone in the channel and online',
|
||||
defaultMessage: 'Notifies everyone in the channel and online'
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -232,10 +232,10 @@ export default class AtMention extends PureComponent {
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
listView: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
search: {
|
||||
minHeight: 125,
|
||||
},
|
||||
minHeight: 125
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
filterMembersInChannel,
|
||||
filterMembersNotInChannel,
|
||||
filterMembersInCurrentTeam,
|
||||
getMatchTermForAtMention,
|
||||
getMatchTermForAtMention
|
||||
} from 'app/selectors/autocomplete';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
@@ -44,15 +44,15 @@ function mapStateToProps(state, ownProps) {
|
||||
inChannel,
|
||||
outChannel,
|
||||
requestStatus: state.requests.users.autocompleteUsers.status,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
autocompleteUsers,
|
||||
}, dispatch),
|
||||
autocompleteUsers
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
@@ -19,7 +19,7 @@ export default class AtMentionItem extends PureComponent {
|
||||
onPress: PropTypes.func.isRequired,
|
||||
userId: PropTypes.string.isRequired,
|
||||
username: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
completeMention = () => {
|
||||
@@ -33,7 +33,7 @@ export default class AtMentionItem extends PureComponent {
|
||||
lastName,
|
||||
userId,
|
||||
username,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
@@ -67,21 +67,21 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
paddingVertical: 8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
rowPicture: {
|
||||
marginHorizontal: 8,
|
||||
width: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
rowUsername: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
rowFullname: {
|
||||
color: theme.centerChannelColor,
|
||||
opacity: 0.6,
|
||||
},
|
||||
opacity: 0.6
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ function mapStateToProps(state, ownProps) {
|
||||
firstName: user.first_name,
|
||||
lastName: user.last_name,
|
||||
username: user.username,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
Keyboard,
|
||||
Platform,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
@@ -24,11 +24,11 @@ export default class Autocomplete extends PureComponent {
|
||||
rootId: PropTypes.string,
|
||||
isSearch: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isSearch: false,
|
||||
isSearch: false
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -37,12 +37,12 @@ export default class Autocomplete extends PureComponent {
|
||||
channelMentionCount: 0,
|
||||
emojiCount: 0,
|
||||
commandCount: 0,
|
||||
keyboardOffset: 0,
|
||||
keyboardOffset: 0
|
||||
};
|
||||
|
||||
handleSelectionChange = (event) => {
|
||||
this.setState({
|
||||
cursorPosition: event.nativeEvent.selection.end,
|
||||
cursorPosition: event.nativeEvent.selection.end
|
||||
});
|
||||
};
|
||||
|
||||
@@ -147,34 +147,34 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
left: 0,
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
right: 0
|
||||
},
|
||||
borders: {
|
||||
borderWidth: 1,
|
||||
borderColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderBottomWidth: 0,
|
||||
borderBottomWidth: 0
|
||||
},
|
||||
bordersSearch: {
|
||||
borderWidth: 1,
|
||||
borderColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
},
|
||||
container: {
|
||||
bottom: 0,
|
||||
maxHeight: 200,
|
||||
maxHeight: 200
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
flex: 1
|
||||
},
|
||||
searchContainer: {
|
||||
flex: 1,
|
||||
...Platform.select({
|
||||
android: {
|
||||
top: 46,
|
||||
top: 46
|
||||
},
|
||||
ios: {
|
||||
top: 44,
|
||||
},
|
||||
}),
|
||||
},
|
||||
top: 44
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
|
||||
export default class AutocompleteDivider extends PureComponent {
|
||||
static propTypes = {
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -26,7 +26,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
},
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import AutocompleteDivider from './autocomplete_divider';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export default class AutocompleteSectionHeader extends PureComponent {
|
||||
static propTypes = {
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -40,15 +40,15 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
paddingLeft: 8,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
},
|
||||
sectionText: {
|
||||
fontSize: 12,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.7),
|
||||
paddingVertical: 7,
|
||||
paddingVertical: 7
|
||||
},
|
||||
sectionWrapper: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
backgroundColor: theme.centerChannelBg
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
export default class ChannelMention extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
searchChannels: PropTypes.func.isRequired,
|
||||
searchChannels: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
cursorPosition: PropTypes.number.isRequired,
|
||||
@@ -31,19 +31,19 @@ export default class ChannelMention extends PureComponent {
|
||||
publicChannels: PropTypes.array,
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isSearch: false,
|
||||
value: '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sections: [],
|
||||
sections: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class ChannelMention extends PureComponent {
|
||||
// if the term changes but is null or the mention has been completed we render this component as null
|
||||
this.setState({
|
||||
mentionComplete: false,
|
||||
sections: [],
|
||||
sections: []
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(0);
|
||||
@@ -83,7 +83,7 @@ export default class ChannelMention extends PureComponent {
|
||||
id: 'suggestion.search.public',
|
||||
defaultMessage: 'Public Channels',
|
||||
data: publicChannels,
|
||||
key: 'publicChannels',
|
||||
key: 'publicChannels'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ export default class ChannelMention extends PureComponent {
|
||||
id: 'suggestion.search.private',
|
||||
defaultMessage: 'Private Channels',
|
||||
data: privateChannels,
|
||||
key: 'privateChannels',
|
||||
key: 'privateChannels'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -101,7 +101,7 @@ export default class ChannelMention extends PureComponent {
|
||||
id: 'suggestion.mention.channels',
|
||||
defaultMessage: 'My Channels',
|
||||
data: myChannels,
|
||||
key: 'myChannels',
|
||||
key: 'myChannels'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,13 +110,13 @@ export default class ChannelMention extends PureComponent {
|
||||
id: 'suggestion.mention.morechannels',
|
||||
defaultMessage: 'Other Channels',
|
||||
data: otherChannels,
|
||||
key: 'otherChannels',
|
||||
key: 'otherChannels'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
sections,
|
||||
sections
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(sections.reduce((total, section) => total + section.data.length, 0));
|
||||
@@ -196,10 +196,10 @@ export default class ChannelMention extends PureComponent {
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
listView: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
search: {
|
||||
minHeight: 125,
|
||||
},
|
||||
minHeight: 125
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
filterOtherChannels,
|
||||
filterPublicChannels,
|
||||
filterPrivateChannels,
|
||||
getMatchTermForChannelMention,
|
||||
getMatchTermForChannelMention
|
||||
} from 'app/selectors/autocomplete';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
@@ -44,15 +44,15 @@ function mapStateToProps(state, ownProps) {
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
matchTerm,
|
||||
requestStatus: state.requests.channels.getChannels.status,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
searchChannels,
|
||||
}, dispatch),
|
||||
searchChannels
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
@@ -16,7 +16,7 @@ export default class ChannelMentionItem extends PureComponent {
|
||||
displayName: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
completeMention = () => {
|
||||
@@ -29,7 +29,7 @@ export default class ChannelMentionItem extends PureComponent {
|
||||
channelId,
|
||||
displayName,
|
||||
name,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
@@ -53,15 +53,15 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
padding: 8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
rowDisplayName: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
rowName: {
|
||||
color: theme.centerChannelColor,
|
||||
opacity: 0.6,
|
||||
},
|
||||
opacity: 0.6
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
displayName: channel.display_name,
|
||||
name: channel.name,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,9 @@ import {
|
||||
FlatList,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
|
||||
|
||||
import AutocompleteDivider from 'app/components/autocomplete/autocomplete_divider';
|
||||
import Emoji from 'app/components/emoji';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
@@ -22,8 +20,7 @@ const EMOJI_REGEX_WITHOUT_PREFIX = /\B(:([^:\s]*))$/i;
|
||||
export default class EmojiSuggestion extends Component {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
addReactionToLatestPost: PropTypes.func.isRequired,
|
||||
autocompleteCustomEmojis: PropTypes.func.isRequired,
|
||||
addReactionToLatestPost: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
cursorPosition: PropTypes.number,
|
||||
emojis: PropTypes.array.isRequired,
|
||||
@@ -33,26 +30,19 @@ export default class EmojiSuggestion extends Component {
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
onResultCountChange: PropTypes.func.isRequired,
|
||||
rootId: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
serverVersion: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
defaultChannel: {},
|
||||
value: '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
state = {
|
||||
active: false,
|
||||
dataSource: [],
|
||||
dataSource: []
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.matchTerm = '';
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isSearch) {
|
||||
return;
|
||||
@@ -64,7 +54,8 @@ export default class EmojiSuggestion extends Component {
|
||||
if (!match || this.state.emojiComplete) {
|
||||
this.setState({
|
||||
active: false,
|
||||
emojiComplete: false,
|
||||
matchTerm: null,
|
||||
emojiComplete: false
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(0);
|
||||
@@ -72,33 +63,18 @@ export default class EmojiSuggestion extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldMatchTerm = this.matchTerm;
|
||||
this.matchTerm = match[3] || '';
|
||||
const matchTerm = match[3];
|
||||
|
||||
// If we're server version 4.7 or higher
|
||||
if (isMinimumServerVersion(this.props.serverVersion, 4, 7)) {
|
||||
if (this.matchTerm !== oldMatchTerm && this.matchTerm.length) {
|
||||
this.props.actions.autocompleteCustomEmojis(this.matchTerm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.emojis !== nextProps.emojis) {
|
||||
this.handleFuzzySearch(this.matchTerm, nextProps);
|
||||
} else if (!this.matchTerm.length) {
|
||||
const initialEmojis = [...nextProps.emojis];
|
||||
initialEmojis.splice(0, 300);
|
||||
const data = initialEmojis.sort();
|
||||
|
||||
this.setEmojiData(data);
|
||||
}
|
||||
|
||||
return;
|
||||
const matchTermChanged = matchTerm !== this.state.matchTerm;
|
||||
if (matchTermChanged) {
|
||||
this.setState({
|
||||
matchTerm
|
||||
});
|
||||
}
|
||||
|
||||
// If we're server version 4.6 or lower
|
||||
if (this.matchTerm !== oldMatchTerm) {
|
||||
this.handleFuzzySearch(this.matchTerm, nextProps);
|
||||
} else if (!this.matchTerm.length) {
|
||||
if (matchTermChanged) {
|
||||
this.handleFuzzySearch(matchTerm, nextProps);
|
||||
} else if (!matchTerm.length) {
|
||||
const initialEmojis = [...nextProps.emojis];
|
||||
initialEmojis.splice(0, 300);
|
||||
const data = initialEmojis.sort();
|
||||
@@ -118,7 +94,7 @@ export default class EmojiSuggestion extends Component {
|
||||
setEmojiData = (data) => {
|
||||
this.setState({
|
||||
active: data.length > 0,
|
||||
dataSource: data,
|
||||
dataSource: data
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(data.length);
|
||||
@@ -143,7 +119,7 @@ export default class EmojiSuggestion extends Component {
|
||||
|
||||
this.setState({
|
||||
active: false,
|
||||
emojiComplete: true,
|
||||
emojiComplete: true
|
||||
});
|
||||
};
|
||||
|
||||
@@ -198,22 +174,22 @@ export default class EmojiSuggestion extends Component {
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
emoji: {
|
||||
marginRight: 5,
|
||||
marginRight: 5
|
||||
},
|
||||
emojiName: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
listView: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
row: {
|
||||
height: 40,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
backgroundColor: theme.centerChannelBg
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,8 +6,6 @@ import {createSelector} from 'reselect';
|
||||
import {bindActionCreators} from 'redux';
|
||||
|
||||
import {getCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
|
||||
import {autocompleteCustomEmojis} from 'mattermost-redux/actions/emojis';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import {addReactionToLatestPost} from 'app/actions/views/emoji';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
@@ -35,7 +33,7 @@ function mapStateToProps(state) {
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 2,
|
||||
maxPatternLength: 32,
|
||||
maxPatternLength: 32
|
||||
};
|
||||
|
||||
const emojis = getEmojisByName(state);
|
||||
@@ -45,17 +43,15 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
fuse,
|
||||
emojis,
|
||||
theme: getTheme(state),
|
||||
serverVersion: state.entities.general.serverVersion || Client4.getServerVersion(),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
addReactionToLatestPost,
|
||||
autocompleteCustomEmojis,
|
||||
}, dispatch),
|
||||
addReactionToLatestPost
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ function mapStateToProps(state) {
|
||||
const {deviceHeight} = getDimensions(state);
|
||||
return {
|
||||
deviceHeight,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -30,15 +30,15 @@ function mapStateToProps(state) {
|
||||
commands: mobileCommandsSelector(state),
|
||||
commandsRequest: state.requests.integrations.getAutocompleteCommands,
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
getAutocompleteCommands,
|
||||
}, dispatch),
|
||||
getAutocompleteCommands
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
FlatList,
|
||||
FlatList
|
||||
} from 'react-native';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
@@ -19,7 +19,7 @@ const TIME_BEFORE_NEXT_COMMAND_REQUEST = 1000 * 60 * 5;
|
||||
export default class SlashSuggestion extends Component {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
getAutocompleteCommands: PropTypes.func.isRequired,
|
||||
getAutocompleteCommands: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
commands: PropTypes.array,
|
||||
@@ -28,19 +28,19 @@ export default class SlashSuggestion extends Component {
|
||||
theme: PropTypes.object.isRequired,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
onResultCountChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
defaultChannel: {},
|
||||
value: '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
state = {
|
||||
active: false,
|
||||
suggestionComplete: false,
|
||||
dataSource: [],
|
||||
lastCommandRequest: 0,
|
||||
lastCommandRequest: 0
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@@ -53,12 +53,12 @@ export default class SlashSuggestion extends Component {
|
||||
commands: nextCommands,
|
||||
commandsRequest: nextCommandsRequest,
|
||||
currentTeamId: nextTeamId,
|
||||
value: nextValue,
|
||||
value: nextValue
|
||||
} = nextProps;
|
||||
|
||||
if (currentTeamId !== nextTeamId) {
|
||||
this.setState({
|
||||
lastCommandRequest: 0,
|
||||
lastCommandRequest: 0
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export default class SlashSuggestion extends Component {
|
||||
this.setState({
|
||||
active: false,
|
||||
matchTerm: null,
|
||||
suggestionComplete: false,
|
||||
suggestionComplete: false
|
||||
});
|
||||
this.props.onResultCountChange(0);
|
||||
return;
|
||||
@@ -79,7 +79,7 @@ export default class SlashSuggestion extends Component {
|
||||
if ((!nextCommands.length || dataIsStale) && nextCommandsRequest.status !== RequestStatus.STARTED) {
|
||||
this.props.actions.getAutocompleteCommands(nextProps.currentTeamId);
|
||||
this.setState({
|
||||
lastCommandRequest: Date.now(),
|
||||
lastCommandRequest: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ export default class SlashSuggestion extends Component {
|
||||
|
||||
this.setState({
|
||||
active: data.length,
|
||||
dataSource: data,
|
||||
dataSource: data
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(data.length);
|
||||
@@ -116,7 +116,7 @@ export default class SlashSuggestion extends Component {
|
||||
|
||||
this.setState({
|
||||
active: false,
|
||||
suggestionComplete: true,
|
||||
suggestionComplete: true
|
||||
});
|
||||
};
|
||||
|
||||
@@ -161,7 +161,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
listView: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
backgroundColor: theme.centerChannelBg
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
@@ -17,7 +17,7 @@ export default class SlashSuggestionItem extends PureComponent {
|
||||
hint: PropTypes.string,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
trigger: PropTypes.string,
|
||||
trigger: PropTypes.string
|
||||
};
|
||||
|
||||
completeSuggestion = () => {
|
||||
@@ -31,7 +31,7 @@ export default class SlashSuggestionItem extends PureComponent {
|
||||
description,
|
||||
hint,
|
||||
theme,
|
||||
trigger,
|
||||
trigger
|
||||
} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
@@ -60,24 +60,24 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
borderLeftWidth: 1,
|
||||
borderLeftColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderRightColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
},
|
||||
rowDisplayName: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
rowName: {
|
||||
color: theme.centerChannelColor,
|
||||
opacity: 0.6,
|
||||
opacity: 0.6
|
||||
},
|
||||
suggestionDescription: {
|
||||
fontSize: 11,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6),
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6)
|
||||
},
|
||||
suggestionName: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
marginBottom: 5,
|
||||
},
|
||||
marginBottom: 5
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class SpecialMentionItem extends PureComponent {
|
||||
id: PropTypes.string.isRequired,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
values: PropTypes.object,
|
||||
values: PropTypes.object
|
||||
};
|
||||
|
||||
completeMention = () => {
|
||||
@@ -34,7 +34,7 @@ export default class SpecialMentionItem extends PureComponent {
|
||||
id,
|
||||
completeHandle,
|
||||
theme,
|
||||
values,
|
||||
values
|
||||
} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
@@ -70,31 +70,31 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
paddingVertical: 8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
rowPicture: {
|
||||
marginHorizontal: 8,
|
||||
width: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
rowIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.7),
|
||||
fontSize: 14,
|
||||
fontSize: 14
|
||||
},
|
||||
rowUsername: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
rowFullname: {
|
||||
color: theme.centerChannelColor,
|
||||
flex: 1,
|
||||
opacity: 0.6,
|
||||
opacity: 0.6
|
||||
},
|
||||
textWrapper: {
|
||||
flex: 1,
|
||||
flexWrap: 'wrap',
|
||||
paddingRight: 8,
|
||||
},
|
||||
paddingRight: 8
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -9,14 +9,14 @@ import {
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
ViewPropTypes,
|
||||
ViewPropTypes
|
||||
} from 'react-native';
|
||||
|
||||
export default class Badge extends PureComponent {
|
||||
static defaultProps = {
|
||||
extraPaddingHorizontal: 10,
|
||||
minHeight: 0,
|
||||
minWidth: 0,
|
||||
minWidth: 0
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
@@ -26,7 +26,7 @@ export default class Badge extends PureComponent {
|
||||
countStyle: Text.propTypes.style,
|
||||
minHeight: PropTypes.number,
|
||||
minWidth: PropTypes.number,
|
||||
onPress: PropTypes.func,
|
||||
onPress: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -42,7 +42,7 @@ export default class Badge extends PureComponent {
|
||||
onMoveShouldSetPanResponder: () => true,
|
||||
onStartShouldSetResponderCapture: () => true,
|
||||
onMoveShouldSetResponderCapture: () => true,
|
||||
onResponderMove: () => false,
|
||||
onResponderMove: () => false
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ export default class Badge extends PureComponent {
|
||||
|
||||
onLayout = (e) => {
|
||||
if (!this.layoutReady) {
|
||||
const height = Math.max(e.nativeEvent.layout.height, this.props.minHeight);
|
||||
const borderRadius = height / 2;
|
||||
let width;
|
||||
|
||||
if (e.nativeEvent.layout.width <= e.nativeEvent.layout.height) {
|
||||
@@ -81,14 +83,14 @@ export default class Badge extends PureComponent {
|
||||
} else {
|
||||
width = e.nativeEvent.layout.width + this.props.extraPaddingHorizontal;
|
||||
}
|
||||
width = Math.max(width + 10, this.props.minWidth);
|
||||
const borderRadius = width / 2;
|
||||
width = Math.max(width, this.props.minWidth);
|
||||
this.setNativeProps({
|
||||
style: {
|
||||
width,
|
||||
height,
|
||||
borderRadius,
|
||||
opacity: 1,
|
||||
},
|
||||
opacity: 1
|
||||
}
|
||||
});
|
||||
this.layoutReady = true;
|
||||
}
|
||||
@@ -139,23 +141,22 @@ export default class Badge extends PureComponent {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
badge: {
|
||||
backgroundColor: '#444',
|
||||
borderRadius: 20,
|
||||
height: 20,
|
||||
top: 2,
|
||||
padding: 12,
|
||||
paddingTop: 3,
|
||||
paddingBottom: 3,
|
||||
backgroundColor: '#444',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
right: 30,
|
||||
top: 2,
|
||||
right: 30
|
||||
},
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
text: {
|
||||
fontSize: 14,
|
||||
color: 'white',
|
||||
},
|
||||
color: 'white'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,19 +11,19 @@ import {GlobalStyles} from 'app/styles';
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
loading: {
|
||||
marginLeft: 3,
|
||||
},
|
||||
marginLeft: 3
|
||||
}
|
||||
});
|
||||
|
||||
export default class Button extends PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
loading: PropTypes.bool,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
onPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
onPress = () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Keyboard,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {General, WebsocketEvents} from 'mattermost-redux/constants';
|
||||
@@ -29,7 +29,7 @@ const {
|
||||
ANDROID_TOP_LANDSCAPE,
|
||||
ANDROID_TOP_PORTRAIT,
|
||||
IOS_TOP_LANDSCAPE,
|
||||
IOS_TOP_PORTRAIT,
|
||||
IOS_TOP_PORTRAIT
|
||||
} = ViewTypes;
|
||||
const DRAWER_INITIAL_OFFSET = 40;
|
||||
const DRAWER_LANDSCAPE_OFFSET = 150;
|
||||
@@ -43,7 +43,7 @@ export default class ChannelDrawer extends Component {
|
||||
makeDirectChannel: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired,
|
||||
setChannelLoading: PropTypes.func.isRequired,
|
||||
setChannelLoading: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
blurPostTextBox: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
@@ -54,7 +54,7 @@ export default class ChannelDrawer extends Component {
|
||||
intl: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
teamsCount: PropTypes.number.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
closeHandle = null;
|
||||
@@ -69,7 +69,7 @@ export default class ChannelDrawer extends Component {
|
||||
openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
|
||||
}
|
||||
this.state = {
|
||||
openDrawerOffset,
|
||||
openDrawerOffset
|
||||
};
|
||||
}
|
||||
|
||||
@@ -175,12 +175,12 @@ export default class ChannelDrawer extends Component {
|
||||
mainOverlay: {
|
||||
backgroundColor: this.props.theme.centerChannelBg,
|
||||
elevation: 3,
|
||||
opacity,
|
||||
opacity
|
||||
},
|
||||
drawerOverlay: {
|
||||
backgroundColor: ratio ? '#000' : '#FFF',
|
||||
opacity: ratio ? (1 - ratio) / 2 : 1,
|
||||
},
|
||||
opacity: ratio ? (1 - ratio) / 2 : 1
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -202,7 +202,7 @@ export default class ChannelDrawer extends Component {
|
||||
|
||||
selectChannel = (channel, currentChannelId) => {
|
||||
const {
|
||||
actions,
|
||||
actions
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@@ -210,17 +210,16 @@ export default class ChannelDrawer extends Component {
|
||||
markChannelAsRead,
|
||||
setChannelLoading,
|
||||
setChannelDisplayName,
|
||||
markChannelAsViewed,
|
||||
markChannelAsViewed
|
||||
} = actions;
|
||||
|
||||
tracker.channelSwitch = Date.now();
|
||||
setChannelLoading(channel.id !== currentChannelId);
|
||||
setChannelDisplayName(channel.display_name);
|
||||
|
||||
this.closeChannelDrawer();
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
setChannelLoading(channel.id !== currentChannelId);
|
||||
setChannelDisplayName(channel.display_name);
|
||||
|
||||
handleSelectChannel(channel.id);
|
||||
requestAnimationFrame(() => {
|
||||
// mark the channel as viewed after all the frame has flushed
|
||||
@@ -237,12 +236,12 @@ export default class ChannelDrawer extends Component {
|
||||
actions,
|
||||
currentTeamId,
|
||||
currentUserId,
|
||||
intl,
|
||||
intl
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
joinChannel,
|
||||
makeDirectChannel,
|
||||
makeDirectChannel
|
||||
} = actions;
|
||||
|
||||
const displayValue = {displayName: channel.display_name};
|
||||
@@ -254,7 +253,7 @@ export default class ChannelDrawer extends Component {
|
||||
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.",
|
||||
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again."
|
||||
};
|
||||
alertErrorWithFallback(intl, result.error, dmFailedMessage, displayValue);
|
||||
}
|
||||
@@ -264,7 +263,7 @@ export default class ChannelDrawer extends Component {
|
||||
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.",
|
||||
defaultMessage: "We couldn't join the channel {displayName}. Please check your connection and try again."
|
||||
};
|
||||
alertErrorWithFallback(intl, result.error, joinFailedMessage, displayValue);
|
||||
}
|
||||
@@ -320,11 +319,11 @@ export default class ChannelDrawer extends Component {
|
||||
const {
|
||||
navigator,
|
||||
teamsCount,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
openDrawerOffset,
|
||||
openDrawerOffset
|
||||
} = this.state;
|
||||
|
||||
const multipleTeams = teamsCount > 1;
|
||||
@@ -429,9 +428,9 @@ export default class ChannelDrawer extends Component {
|
||||
shadowRadius: 12,
|
||||
shadowOffset: {
|
||||
width: -4,
|
||||
height: 0,
|
||||
},
|
||||
},
|
||||
height: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@@ -443,6 +442,6 @@ export default class ChannelDrawer extends Component {
|
||||
const style = StyleSheet.create({
|
||||
swiperContent: {
|
||||
flex: 1,
|
||||
marginBottom: 10,
|
||||
},
|
||||
marginBottom: 10
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
Platform,
|
||||
TouchableHighlight,
|
||||
Text,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import ChannelIcon from 'app/components/channel_icon';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
@@ -31,16 +31,15 @@ export default class ChannelItem extends PureComponent {
|
||||
navigator: PropTypes.object,
|
||||
onSelectChannel: PropTypes.func.isRequired,
|
||||
status: PropTypes.string,
|
||||
teammateDeletedAt: PropTypes.number,
|
||||
type: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
onPress = preventDoubleTap(() => {
|
||||
onPress = wrapWithPreventDoubleTap(() => {
|
||||
const {channelId, currentChannelId, displayName, fake, onSelectChannel, type} = this.props;
|
||||
requestAnimationFrame(() => {
|
||||
onSelectChannel({id: channelId, display_name: displayName, fake, type}, currentChannelId);
|
||||
@@ -58,11 +57,11 @@ export default class ChannelItem extends PureComponent {
|
||||
previewView: this.previewRef,
|
||||
previewActions: [{
|
||||
id: 'action-mark-as-read',
|
||||
title: intl.formatMessage({id: 'mobile.channel.markAsRead', defaultMessage: 'Mark As Read'}),
|
||||
title: intl.formatMessage({id: 'mobile.channel.markAsRead', defaultMessage: 'Mark As Read'})
|
||||
}],
|
||||
passProps: {
|
||||
channelId,
|
||||
},
|
||||
channelId
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -80,9 +79,8 @@ export default class ChannelItem extends PureComponent {
|
||||
isUnread,
|
||||
mentions,
|
||||
status,
|
||||
teammateDeletedAt,
|
||||
theme,
|
||||
type,
|
||||
type
|
||||
} = this.props;
|
||||
|
||||
const {intl} = this.context;
|
||||
@@ -91,7 +89,7 @@ export default class ChannelItem extends PureComponent {
|
||||
if (isMyUser) {
|
||||
channelDisplayName = intl.formatMessage({
|
||||
id: 'channel_header.directchannel.you',
|
||||
defaultMessage: '{displayName} (you)',
|
||||
defaultMessage: '{displayName} (you)'
|
||||
}, {displayname: displayName});
|
||||
}
|
||||
|
||||
@@ -135,7 +133,6 @@ export default class ChannelItem extends PureComponent {
|
||||
membersCount={displayName.split(',').length}
|
||||
size={16}
|
||||
status={status}
|
||||
teammateDeletedAt={teammateDeletedAt}
|
||||
theme={theme}
|
||||
type={type}
|
||||
/>
|
||||
@@ -173,22 +170,22 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 44,
|
||||
height: 44
|
||||
},
|
||||
borderActive: {
|
||||
backgroundColor: theme.sidebarTextActiveBorder,
|
||||
width: 5,
|
||||
width: 5
|
||||
},
|
||||
item: {
|
||||
alignItems: 'center',
|
||||
height: 44,
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingLeft: 16,
|
||||
paddingLeft: 16
|
||||
},
|
||||
itemActive: {
|
||||
backgroundColor: changeOpacity(theme.sidebarTextActiveColor, 0.1),
|
||||
paddingLeft: 11,
|
||||
paddingLeft: 11
|
||||
},
|
||||
text: {
|
||||
color: changeOpacity(theme.sidebarText, 0.4),
|
||||
@@ -196,13 +193,13 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
lineHeight: 16,
|
||||
paddingRight: 40,
|
||||
paddingRight: 40
|
||||
},
|
||||
textActive: {
|
||||
color: theme.sidebarTextActiveColor,
|
||||
color: theme.sidebarTextActiveColor
|
||||
},
|
||||
textUnread: {
|
||||
color: theme.sidebarUnreadText,
|
||||
color: theme.sidebarUnreadText
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: theme.mentionBj,
|
||||
@@ -211,11 +208,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
borderWidth: 1,
|
||||
padding: 3,
|
||||
position: 'relative',
|
||||
right: 16,
|
||||
right: 16
|
||||
},
|
||||
mention: {
|
||||
color: theme.mentionColor,
|
||||
fontSize: 10,
|
||||
},
|
||||
fontSize: 10
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import {connect} from 'react-redux';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getCurrentChannelId, makeGetChannel, getMyChannelMember} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import ChannelItem from './channel_item';
|
||||
|
||||
@@ -22,13 +22,8 @@ function makeMapStateToProps() {
|
||||
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
let isMyUser = false;
|
||||
let teammateDeletedAt = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -38,9 +33,8 @@ function makeMapStateToProps() {
|
||||
isMyUser,
|
||||
mentions: member ? member.mention_count : 0,
|
||||
status: channel.status,
|
||||
teammateDeletedAt,
|
||||
theme: getTheme(state),
|
||||
type: channel.type,
|
||||
type: channel.type
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Platform,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
@@ -29,7 +29,7 @@ class ChannelsList extends React.PureComponent {
|
||||
onSearchStart: PropTypes.func.isRequired,
|
||||
onSelectChannel: PropTypes.func.isRequired,
|
||||
onShowTeams: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -37,7 +37,7 @@ class ChannelsList extends React.PureComponent {
|
||||
|
||||
this.state = {
|
||||
searching: false,
|
||||
term: '',
|
||||
term: ''
|
||||
};
|
||||
|
||||
MaterialIcon.getImageSource('close', 20, this.props.theme.sidebarHeaderTextColor).then((source) => {
|
||||
@@ -77,7 +77,7 @@ class ChannelsList extends React.PureComponent {
|
||||
intl,
|
||||
navigator,
|
||||
onShowTeams,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
|
||||
const {searching, term} = this.state;
|
||||
@@ -106,11 +106,7 @@ class ChannelsList extends React.PureComponent {
|
||||
backgroundColor: changeOpacity(theme.sidebarHeaderTextColor, 0.2),
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
fontSize: 15,
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: -5,
|
||||
},
|
||||
}),
|
||||
lineHeight: 66
|
||||
};
|
||||
|
||||
const title = (
|
||||
@@ -120,7 +116,7 @@ class ChannelsList extends React.PureComponent {
|
||||
placeholder={intl.formatMessage({id: 'mobile.channel_drawer.search', defaultMessage: 'Jump to...'})}
|
||||
cancelTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
|
||||
backgroundColor='transparent'
|
||||
inputHeight={34}
|
||||
inputHeight={33}
|
||||
inputStyle={searchBarInput}
|
||||
placeholderTextColor={changeOpacity(theme.sidebarHeaderTextColor, 0.5)}
|
||||
tintColorSearch={changeOpacity(theme.sidebarHeaderTextColor, 0.5)}
|
||||
@@ -161,10 +157,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: theme.sidebarBg,
|
||||
flex: 1,
|
||||
flex: 1
|
||||
},
|
||||
statusBar: {
|
||||
backgroundColor: theme.sidebarHeaderBg,
|
||||
backgroundColor: theme.sidebarHeaderBg
|
||||
},
|
||||
headerContainer: {
|
||||
alignItems: 'center',
|
||||
@@ -175,30 +171,30 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
borderBottomColor: changeOpacity(theme.sidebarHeaderTextColor, 0.10),
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: ANDROID_TOP_PORTRAIT,
|
||||
height: ANDROID_TOP_PORTRAIT
|
||||
},
|
||||
ios: {
|
||||
height: 44,
|
||||
},
|
||||
}),
|
||||
height: 44
|
||||
}
|
||||
})
|
||||
},
|
||||
header: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
flex: 1,
|
||||
fontSize: 17,
|
||||
fontWeight: 'normal',
|
||||
paddingLeft: 16,
|
||||
paddingLeft: 16
|
||||
},
|
||||
switchContainer: {
|
||||
position: 'relative',
|
||||
top: -1,
|
||||
top: -1
|
||||
},
|
||||
titleContainer: { // These aren't used by this component, but they are passed down to the list component
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
marginLeft: 16,
|
||||
marginLeft: 16
|
||||
},
|
||||
title: {
|
||||
flex: 1,
|
||||
@@ -207,40 +203,40 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
fontSize: 15,
|
||||
fontWeight: '400',
|
||||
letterSpacing: 0.8,
|
||||
lineHeight: 18,
|
||||
lineHeight: 18
|
||||
},
|
||||
searchContainer: {
|
||||
flex: 1,
|
||||
paddingRight: 10,
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: 1,
|
||||
marginBottom: 1
|
||||
},
|
||||
ios: {
|
||||
marginBottom: 3,
|
||||
},
|
||||
}),
|
||||
marginBottom: 3
|
||||
}
|
||||
})
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.sidebarText, 0.1),
|
||||
height: 1,
|
||||
height: 1
|
||||
},
|
||||
actionContainer: {
|
||||
alignItems: 'center',
|
||||
height: 48,
|
||||
justifyContent: 'center',
|
||||
width: 50,
|
||||
width: 50
|
||||
},
|
||||
action: {
|
||||
color: theme.sidebarText,
|
||||
fontSize: 20,
|
||||
fontWeight: '500',
|
||||
lineHeight: 18,
|
||||
lineHeight: 18
|
||||
},
|
||||
above: {
|
||||
backgroundColor: theme.mentionBj,
|
||||
top: 9,
|
||||
},
|
||||
top: 9
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
FlatList,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
@@ -30,7 +30,7 @@ class FilteredList extends Component {
|
||||
getProfilesInTeam: PropTypes.func.isRequired,
|
||||
makeGroupMessageVisibleIfNecessary: PropTypes.func.isRequired,
|
||||
searchChannels: PropTypes.func.isRequired,
|
||||
searchProfiles: PropTypes.func.isRequired,
|
||||
searchProfiles: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
channels: PropTypes.object.isRequired,
|
||||
currentTeam: PropTypes.object.isRequired,
|
||||
@@ -49,18 +49,19 @@ class FilteredList extends Component {
|
||||
statuses: PropTypes.object,
|
||||
styles: PropTypes.object.isRequired,
|
||||
term: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
currentTeam: {},
|
||||
currentChannel: {},
|
||||
pastDirectMessages: [],
|
||||
pastDirectMessages: []
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataSource: this.buildData(props),
|
||||
dataSource: this.buildData(props)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -145,28 +146,28 @@ class FilteredList extends Component {
|
||||
unreads: {
|
||||
builder: this.buildUnreadChannelsForSearch,
|
||||
id: 'mobile.channel_list.unreads',
|
||||
defaultMessage: 'UNREADS',
|
||||
defaultMessage: 'UNREADS'
|
||||
},
|
||||
channels: {
|
||||
builder: this.buildChannelsForSearch,
|
||||
id: 'mobile.channel_list.channels',
|
||||
defaultMessage: 'CHANNELS',
|
||||
defaultMessage: 'CHANNELS'
|
||||
},
|
||||
dms: {
|
||||
builder: this.buildCurrentDMSForSearch,
|
||||
id: 'sidebar.direct',
|
||||
defaultMessage: 'DIRECT MESSAGES',
|
||||
defaultMessage: 'DIRECT MESSAGES'
|
||||
},
|
||||
members: {
|
||||
builder: this.buildMembersForSearch,
|
||||
id: 'mobile.channel_list.members',
|
||||
defaultMessage: 'MEMBERS',
|
||||
defaultMessage: 'MEMBERS'
|
||||
},
|
||||
nonmembers: {
|
||||
builder: this.buildOtherMembersForSearch,
|
||||
id: 'mobile.channel_list.not_member',
|
||||
defaultMessage: 'NOT A MEMBER',
|
||||
},
|
||||
defaultMessage: 'NOT A MEMBER'
|
||||
}
|
||||
});
|
||||
|
||||
buildUnreadChannelsForSearch = (props, term) => {
|
||||
@@ -211,14 +212,14 @@ class FilteredList extends Component {
|
||||
type: General.DM_CHANNEL,
|
||||
fake: true,
|
||||
nickname: u.nickname,
|
||||
fullname: `${u.first_name} ${u.last_name}`,
|
||||
fullname: `${u.first_name} ${u.last_name}`
|
||||
};
|
||||
});
|
||||
|
||||
groupChannels = groupChannels.map((channel) => {
|
||||
return {
|
||||
...channel,
|
||||
...groupChannelMemberDetails[channel.id],
|
||||
...groupChannelMemberDetails[channel.id]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -252,7 +253,7 @@ class FilteredList extends Component {
|
||||
type: General.DM_CHANNEL,
|
||||
fake: true,
|
||||
nickname: u.nickname,
|
||||
fullname: `${u.first_name} ${u.last_name}`,
|
||||
fullname: `${u.first_name} ${u.last_name}`
|
||||
};
|
||||
});
|
||||
|
||||
@@ -265,7 +266,7 @@ class FilteredList extends Component {
|
||||
const {
|
||||
favoriteChannels,
|
||||
publicChannels,
|
||||
privateChannels,
|
||||
privateChannels
|
||||
} = props.channels;
|
||||
|
||||
const favorites = favoriteChannels.filter((c) => {
|
||||
@@ -282,7 +283,7 @@ class FilteredList extends Component {
|
||||
const notMemberOf = otherChannels.map((o) => {
|
||||
return {
|
||||
...o,
|
||||
fake: true,
|
||||
fake: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -368,7 +369,7 @@ class FilteredList extends Component {
|
||||
</View>
|
||||
{bottomDivider && this.renderDivider(styles, 16)}
|
||||
</View>
|
||||
),
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ import {
|
||||
getChannelsWithUnreadSection,
|
||||
getCurrentChannel,
|
||||
getGroupChannels,
|
||||
getOtherChannels,
|
||||
getOtherChannels
|
||||
} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentUserId, getProfilesInCurrentTeam, getUsers, getUserIdsInChannels, getUserStatuses} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getDirectShowPreferences, getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
@@ -73,14 +72,14 @@ function getGroupDetails(currentUserId, userIdsInChannels, profiles, groupChanne
|
||||
email: [],
|
||||
fullname: [],
|
||||
nickname: [],
|
||||
username: [],
|
||||
username: []
|
||||
});
|
||||
|
||||
groupMemberDetails[channel.id] = {
|
||||
email: members.email.join(','),
|
||||
fullname: members.fullname.join(','),
|
||||
nickname: members.nickname.join(','),
|
||||
username: members.username.join(','),
|
||||
username: members.username.join(',')
|
||||
};
|
||||
|
||||
return groupMemberDetails;
|
||||
@@ -110,7 +109,6 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
channels: getChannelsWithUnreadSection(state),
|
||||
currentChannel: getCurrentChannel(state),
|
||||
currentTeam: getCurrentTeam(state),
|
||||
currentUserId,
|
||||
otherChannels: getOtherChannels(state),
|
||||
groupChannelMemberDetails: getGroupChannelMemberDetails(state),
|
||||
@@ -121,7 +119,7 @@ function mapStateToProps(state) {
|
||||
searchOrder,
|
||||
pastDirectMessages: pastDirectMessages(state),
|
||||
restrictDms,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,8 +129,8 @@ function mapDispatchToProps(dispatch) {
|
||||
getProfilesInTeam,
|
||||
makeGroupMessageVisibleIfNecessary,
|
||||
searchChannels,
|
||||
searchProfiles,
|
||||
}, dispatch),
|
||||
searchProfiles
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import ChannelsList from './channels_list';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
getSortedFavoriteChannelIds,
|
||||
getSortedPublicChannelIds,
|
||||
getSortedPrivateChannelIds,
|
||||
getSortedDirectChannelIds,
|
||||
getSortedDirectChannelIds
|
||||
} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getTheme, getFavoritesPreferences} from 'mattermost-redux/selectors/entities/preferences';
|
||||
@@ -34,7 +34,7 @@ function mapStateToProps(state) {
|
||||
publicChannelIds,
|
||||
privateChannelIds,
|
||||
directChannelIds,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
SectionList,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
@@ -19,12 +19,12 @@ import {debounce} from 'mattermost-redux/actions/helpers';
|
||||
import ChannelItem from 'app/components/channel_drawer/channels_list/channel_item';
|
||||
import UnreadIndicator from 'app/components/channel_drawer/channels_list/unread_indicator';
|
||||
import {ListTypes} from 'app/constants';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
|
||||
const VIEWABILITY_CONFIG = {
|
||||
...ListTypes.VISIBILITY_CONFIG_DEFAULTS,
|
||||
waitForInteraction: true,
|
||||
waitForInteraction: true
|
||||
};
|
||||
|
||||
export default class List extends PureComponent {
|
||||
@@ -38,11 +38,11 @@ export default class List extends PureComponent {
|
||||
privateChannelIds: PropTypes.array.isRequired,
|
||||
styles: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
unreadChannelIds: PropTypes.array.isRequired,
|
||||
unreadChannelIds: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -51,7 +51,7 @@ export default class List extends PureComponent {
|
||||
this.state = {
|
||||
sections: this.buildSections(props),
|
||||
showIndicator: false,
|
||||
width: 0,
|
||||
width: 0
|
||||
};
|
||||
|
||||
MaterialIcon.getImageSource('close', 20, this.props.theme.sidebarHeaderTextColor).then((source) => {
|
||||
@@ -66,7 +66,7 @@ export default class List extends PureComponent {
|
||||
favoriteChannelIds,
|
||||
publicChannelIds,
|
||||
privateChannelIds,
|
||||
unreadChannelIds,
|
||||
unreadChannelIds
|
||||
} = this.props;
|
||||
|
||||
if (nextProps.canCreatePrivateChannels !== canCreatePrivateChannels ||
|
||||
@@ -93,7 +93,7 @@ export default class List extends PureComponent {
|
||||
favoriteChannelIds,
|
||||
publicChannelIds,
|
||||
privateChannelIds,
|
||||
unreadChannelIds,
|
||||
unreadChannelIds
|
||||
} = props;
|
||||
const sections = [];
|
||||
|
||||
@@ -104,7 +104,7 @@ export default class List extends PureComponent {
|
||||
data: unreadChannelIds,
|
||||
renderItem: this.renderUnreadItem,
|
||||
topSeparator: false,
|
||||
bottomSeparator: true,
|
||||
bottomSeparator: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export default class List extends PureComponent {
|
||||
defaultMessage: 'FAVORITES',
|
||||
data: favoriteChannelIds,
|
||||
topSeparator: unreadChannelIds.length > 0,
|
||||
bottomSeparator: true,
|
||||
bottomSeparator: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export default class List extends PureComponent {
|
||||
defaultMessage: 'PUBLIC CHANNELS',
|
||||
data: publicChannelIds,
|
||||
topSeparator: favoriteChannelIds.length > 0 || unreadChannelIds.length > 0,
|
||||
bottomSeparator: publicChannelIds.length > 0,
|
||||
bottomSeparator: publicChannelIds.length > 0
|
||||
});
|
||||
|
||||
sections.push({
|
||||
@@ -133,7 +133,7 @@ export default class List extends PureComponent {
|
||||
defaultMessage: 'PRIVATE CHANNELS',
|
||||
data: privateChannelIds,
|
||||
topSeparator: true,
|
||||
bottomSeparator: privateChannelIds.length > 0,
|
||||
bottomSeparator: privateChannelIds.length > 0
|
||||
});
|
||||
|
||||
sections.push({
|
||||
@@ -142,13 +142,13 @@ export default class List extends PureComponent {
|
||||
defaultMessage: 'DIRECT MESSAGES',
|
||||
data: directChannelIds,
|
||||
topSeparator: true,
|
||||
bottomSeparator: directChannelIds.length > 0,
|
||||
bottomSeparator: directChannelIds.length > 0
|
||||
});
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
goToCreatePrivateChannel = preventDoubleTap(() => {
|
||||
goToCreatePrivateChannel = wrapWithPreventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {intl} = this.context;
|
||||
|
||||
@@ -162,16 +162,16 @@ export default class List extends PureComponent {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
channelType: General.PRIVATE_CHANNEL,
|
||||
closeButton: this.closeButton,
|
||||
},
|
||||
closeButton: this.closeButton
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
goToDirectMessages = preventDoubleTap(() => {
|
||||
goToDirectMessages = wrapWithPreventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {intl} = this.context;
|
||||
|
||||
@@ -185,18 +185,18 @@ export default class List extends PureComponent {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
navigatorButtons: {
|
||||
leftButtons: [{
|
||||
id: 'close-dms',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
},
|
||||
icon: this.closeButton
|
||||
}]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
goToMoreChannels = preventDoubleTap(() => {
|
||||
goToMoreChannels = wrapWithPreventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {intl} = this.context;
|
||||
|
||||
@@ -210,11 +210,11 @@ export default class List extends PureComponent {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
closeButton: this.closeButton,
|
||||
},
|
||||
closeButton: this.closeButton
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -282,7 +282,7 @@ export default class List extends PureComponent {
|
||||
bottomSeparator,
|
||||
defaultMessage,
|
||||
id,
|
||||
topSeparator,
|
||||
topSeparator
|
||||
} = section;
|
||||
|
||||
return (
|
||||
@@ -304,7 +304,7 @@ export default class List extends PureComponent {
|
||||
this.refs.list._wrapperListRef.getListRef().scrollToOffset({ //eslint-disable-line no-underscore-dangle
|
||||
x: 0,
|
||||
y: 0,
|
||||
animated: true,
|
||||
animated: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,9 +13,10 @@ function mapStateToProps(state) {
|
||||
|
||||
return {
|
||||
currentTeamId: team.id,
|
||||
displayName: team.display_name,
|
||||
mentionCount: getChannelDrawerBadgeCount(state),
|
||||
teamsCount: getMyTeamsCount(state),
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,38 +4,39 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import TeamIcon from 'app/components/team_icon';
|
||||
|
||||
export default class SwitchTeamsButton extends React.PureComponent {
|
||||
static propTypes = {
|
||||
currentTeamId: PropTypes.string,
|
||||
displayName: PropTypes.string,
|
||||
searching: PropTypes.bool.isRequired,
|
||||
onShowTeams: PropTypes.func.isRequired,
|
||||
mentionCount: PropTypes.number.isRequired,
|
||||
teamsCount: PropTypes.number.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
showTeams = preventDoubleTap(() => {
|
||||
showTeams = wrapWithPreventDoubleTap(() => {
|
||||
this.props.onShowTeams();
|
||||
});
|
||||
|
||||
render() {
|
||||
const {
|
||||
currentTeamId,
|
||||
displayName,
|
||||
mentionCount,
|
||||
searching,
|
||||
teamsCount,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
|
||||
if (!currentTeamId) {
|
||||
@@ -68,14 +69,12 @@ export default class SwitchTeamsButton extends React.PureComponent {
|
||||
<AwesomeIcon
|
||||
name='chevron-left'
|
||||
size={12}
|
||||
style={styles.switcherArrow}
|
||||
color={theme.sidebarHeaderBg}
|
||||
/>
|
||||
<View style={styles.switcherDivider}/>
|
||||
<TeamIcon
|
||||
teamId={currentTeamId}
|
||||
styleContainer={styles.teamIconContainer}
|
||||
styleText={styles.teamIconText}
|
||||
/>
|
||||
<Text style={styles.switcherTeam}>
|
||||
{displayName.substr(0, 2).toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
{badge}
|
||||
@@ -87,33 +86,26 @@ export default class SwitchTeamsButton extends React.PureComponent {
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
switcherContainer: {
|
||||
backgroundColor: theme.sidebarHeaderTextColor,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: 32,
|
||||
backgroundColor: theme.sidebarHeaderTextColor,
|
||||
borderRadius: 2,
|
||||
flexDirection: 'row',
|
||||
height: 32,
|
||||
justifyContent: 'center',
|
||||
marginLeft: 6,
|
||||
marginRight: 6,
|
||||
paddingHorizontal: 3,
|
||||
},
|
||||
switcherArrow: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
marginRight: 3,
|
||||
marginRight: 5,
|
||||
paddingHorizontal: 6
|
||||
},
|
||||
switcherDivider: {
|
||||
backgroundColor: theme.sidebarHeaderBg,
|
||||
height: 15,
|
||||
marginHorizontal: 6,
|
||||
width: 1,
|
||||
width: 1
|
||||
},
|
||||
teamIconContainer: {
|
||||
width: 26,
|
||||
height: 26,
|
||||
marginLeft: 3,
|
||||
},
|
||||
teamIconText: {
|
||||
fontSize: 14,
|
||||
switcherTeam: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
fontFamily: 'OpenSans',
|
||||
fontSize: 14
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: theme.mentionBj,
|
||||
@@ -124,11 +116,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
padding: 3,
|
||||
position: 'absolute',
|
||||
left: -5,
|
||||
top: -5,
|
||||
top: -5
|
||||
},
|
||||
mention: {
|
||||
color: theme.mentionColor,
|
||||
fontSize: 10,
|
||||
},
|
||||
fontSize: 10
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
ViewPropTypes,
|
||||
ViewPropTypes
|
||||
} from 'react-native';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
@@ -18,11 +18,11 @@ export default class UnreadIndicator extends PureComponent {
|
||||
show: PropTypes.bool,
|
||||
style: ViewPropTypes.style,
|
||||
onPress: PropTypes.func,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onPress: () => true,
|
||||
onPress: () => true
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -65,7 +65,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
position: 'absolute',
|
||||
borderRadius: 15,
|
||||
marginHorizontal: 15,
|
||||
height: 25,
|
||||
height: 25
|
||||
},
|
||||
indicatorText: {
|
||||
backgroundColor: 'transparent',
|
||||
@@ -74,11 +74,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
paddingVertical: 2,
|
||||
paddingHorizontal: 4,
|
||||
textAlign: 'center',
|
||||
textAlignVertical: 'center',
|
||||
textAlignVertical: 'center'
|
||||
},
|
||||
arrow: {
|
||||
position: 'relative',
|
||||
bottom: -1,
|
||||
},
|
||||
bottom: -1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -16,12 +16,12 @@ export default class DrawerSwiper extends Component {
|
||||
onPageSelected: PropTypes.func,
|
||||
openDrawerOffset: PropTypes.number,
|
||||
showTeams: PropTypes.bool.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onPageSelected: () => true,
|
||||
openDrawerOffset: 0,
|
||||
openDrawerOffset: 0
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
@@ -64,7 +64,7 @@ export default class DrawerSwiper extends Component {
|
||||
deviceWidth,
|
||||
openDrawerOffset,
|
||||
showTeams,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
|
||||
const initialPage = React.Children.count(children) - 1;
|
||||
@@ -92,6 +92,6 @@ export default class DrawerSwiper extends Component {
|
||||
const style = StyleSheet.create({
|
||||
pagination: {
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
},
|
||||
position: 'absolute'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import DraweSwiper from './drawer_swiper';
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
...getDimensions(state),
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ function mapStateToProps(state) {
|
||||
isLandscape: isLandscape(state),
|
||||
isTablet: isTablet(state),
|
||||
teamsCount: getMyTeamsCount(state),
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ function mapDispatchToProps(dispatch) {
|
||||
makeDirectChannel,
|
||||
markChannelAsRead,
|
||||
setChannelDisplayName,
|
||||
setChannelLoading,
|
||||
}, dispatch),
|
||||
setChannelLoading
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentTeamId, getMySortedTeamIds} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentTeamId, getJoinableTeamIds, getMySortedTeamIds} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
@@ -18,18 +18,19 @@ function mapStateToProps(state) {
|
||||
const locale = getCurrentLocale(state);
|
||||
|
||||
return {
|
||||
canJoinOtherTeams: getJoinableTeamIds(state).length > 0,
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUrl: removeProtocol(getCurrentUrl(state)),
|
||||
teamIds: getMySortedTeamIds(state, locale),
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
handleTeamChange,
|
||||
}, dispatch),
|
||||
handleTeamChange
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ import {
|
||||
StatusBar,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {ListTypes, ViewTypes} from 'app/constants';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
|
||||
@@ -25,21 +25,22 @@ import TeamsListItem from './teams_list_item';
|
||||
const {ANDROID_TOP_PORTRAIT} = ViewTypes;
|
||||
const VIEWABILITY_CONFIG = {
|
||||
...ListTypes.VISIBILITY_CONFIG_DEFAULTS,
|
||||
waitForInteraction: true,
|
||||
waitForInteraction: true
|
||||
};
|
||||
|
||||
class TeamsList extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
handleTeamChange: PropTypes.func.isRequired,
|
||||
handleTeamChange: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
canJoinOtherTeams: PropTypes.bool.isRequired,
|
||||
closeChannelDrawer: PropTypes.func.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
teamIds: PropTypes.array.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -63,7 +64,7 @@ class TeamsList extends PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
goToSelectTeam = preventDoubleTap(() => {
|
||||
goToSelectTeam = wrapWithPreventDoubleTap(() => {
|
||||
const {currentUrl, intl, navigator, theme} = this.props;
|
||||
|
||||
navigator.showModal({
|
||||
@@ -76,18 +77,18 @@ class TeamsList extends PureComponent {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
navigatorButtons: {
|
||||
leftButtons: [{
|
||||
id: 'close-teams',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
icon: this.closeButton
|
||||
}]
|
||||
},
|
||||
passProps: {
|
||||
currentUrl,
|
||||
theme,
|
||||
},
|
||||
theme
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,22 +106,25 @@ class TeamsList extends PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {teamIds, theme} = this.props;
|
||||
const {canJoinOtherTeams, teamIds, theme} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const moreAction = (
|
||||
<TouchableHighlight
|
||||
style={styles.moreActionContainer}
|
||||
onPress={this.goToSelectTeam}
|
||||
underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
|
||||
>
|
||||
<Text
|
||||
style={styles.moreAction}
|
||||
let moreAction;
|
||||
if (canJoinOtherTeams) {
|
||||
moreAction = (
|
||||
<TouchableHighlight
|
||||
style={styles.moreActionContainer}
|
||||
onPress={this.goToSelectTeam}
|
||||
underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
|
||||
>
|
||||
{'+'}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
<Text
|
||||
style={styles.moreAction}
|
||||
>
|
||||
{'+'}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@@ -149,10 +153,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: theme.sidebarBg,
|
||||
flex: 1,
|
||||
flex: 1
|
||||
},
|
||||
statusBar: {
|
||||
backgroundColor: theme.sidebarHeaderBg,
|
||||
backgroundColor: theme.sidebarHeaderBg
|
||||
},
|
||||
headerContainer: {
|
||||
alignItems: 'center',
|
||||
@@ -162,19 +166,19 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
borderBottomColor: changeOpacity(theme.sidebarHeaderTextColor, 0.10),
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: ANDROID_TOP_PORTRAIT,
|
||||
height: ANDROID_TOP_PORTRAIT
|
||||
},
|
||||
ios: {
|
||||
height: 44,
|
||||
},
|
||||
}),
|
||||
height: 44
|
||||
}
|
||||
})
|
||||
},
|
||||
header: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
flex: 1,
|
||||
fontSize: 17,
|
||||
textAlign: 'center',
|
||||
fontWeight: '600',
|
||||
fontWeight: '600'
|
||||
},
|
||||
moreActionContainer: {
|
||||
alignItems: 'center',
|
||||
@@ -182,17 +186,17 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
width: 50,
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: ANDROID_TOP_PORTRAIT,
|
||||
height: ANDROID_TOP_PORTRAIT
|
||||
},
|
||||
ios: {
|
||||
height: 44,
|
||||
},
|
||||
}),
|
||||
height: 44
|
||||
}
|
||||
})
|
||||
},
|
||||
moreAction: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
fontSize: 30,
|
||||
},
|
||||
fontSize: 30
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ function makeMapStateToProps() {
|
||||
displayName: team.display_name,
|
||||
mentionCount: getMentionCount(state, ownProps.teamId),
|
||||
name: team.name,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import TeamIcon from 'app/components/team_icon';
|
||||
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class TeamsListItem extends React.PureComponent {
|
||||
@@ -25,10 +23,10 @@ export default class TeamsListItem extends React.PureComponent {
|
||||
name: PropTypes.string.isRequired,
|
||||
selectTeam: PropTypes.func.isRequired,
|
||||
teamId: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
selectTeam = preventDoubleTap(() => {
|
||||
selectTeam = wrapWithPreventDoubleTap(() => {
|
||||
this.props.selectTeam(this.props.teamId);
|
||||
});
|
||||
|
||||
@@ -40,7 +38,7 @@ export default class TeamsListItem extends React.PureComponent {
|
||||
mentionCount,
|
||||
name,
|
||||
teamId,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
@@ -73,11 +71,11 @@ export default class TeamsListItem extends React.PureComponent {
|
||||
onPress={this.selectTeam}
|
||||
>
|
||||
<View style={styles.teamContainer}>
|
||||
<TeamIcon
|
||||
teamId={teamId}
|
||||
styleContainer={styles.teamIconContainer}
|
||||
styleText={styles.teamIconText}
|
||||
/>
|
||||
<View style={styles.teamIconContainer}>
|
||||
<Text style={styles.teamIcon}>
|
||||
{displayName.substr(0, 2).toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.teamNameContainer}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
@@ -106,40 +104,47 @@ export default class TeamsListItem extends React.PureComponent {
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
teamWrapper: {
|
||||
marginTop: 20,
|
||||
marginTop: 20
|
||||
},
|
||||
teamContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: 16,
|
||||
marginHorizontal: 16
|
||||
},
|
||||
teamIconContainer: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.sidebarText,
|
||||
borderRadius: 2,
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
width: 40
|
||||
},
|
||||
teamIcon: {
|
||||
color: theme.sidebarBg,
|
||||
fontFamily: 'OpenSans',
|
||||
fontSize: 18,
|
||||
fontWeight: '600'
|
||||
},
|
||||
teamNameContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
marginLeft: 10,
|
||||
marginLeft: 10
|
||||
},
|
||||
teamName: {
|
||||
color: theme.sidebarText,
|
||||
fontSize: 18,
|
||||
},
|
||||
teamIconContainer: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
},
|
||||
teamIconText: {
|
||||
fontSize: 18,
|
||||
fontSize: 18
|
||||
},
|
||||
teamUrl: {
|
||||
color: changeOpacity(theme.sidebarText, 0.5),
|
||||
fontSize: 12,
|
||||
fontSize: 12
|
||||
},
|
||||
checkmarkContainer: {
|
||||
alignItems: 'flex-end',
|
||||
alignItems: 'flex-end'
|
||||
},
|
||||
checkmark: {
|
||||
color: theme.sidebarText,
|
||||
fontSize: 20,
|
||||
fontSize: 20
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: theme.mentionBj,
|
||||
@@ -150,11 +155,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
padding: 3,
|
||||
position: 'absolute',
|
||||
left: 45,
|
||||
top: -7.5,
|
||||
top: -7.5
|
||||
},
|
||||
mention: {
|
||||
color: theme.mentionColor,
|
||||
fontSize: 10,
|
||||
},
|
||||
fontSize: 10
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -5,17 +5,11 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import {
|
||||
ArchiveIcon,
|
||||
AwayAvatar,
|
||||
DndAvatar,
|
||||
OfflineAvatar,
|
||||
OnlineAvatar,
|
||||
} from 'app/components/status_icons';
|
||||
import {AwayAvatar, DndAvatar, OfflineAvatar, OnlineAvatar} from 'app/components/status_icons';
|
||||
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
@@ -29,30 +23,19 @@ export default class ChannelIcon extends React.PureComponent {
|
||||
membersCount: PropTypes.number,
|
||||
size: PropTypes.number,
|
||||
status: PropTypes.string,
|
||||
teammateDeletedAt: PropTypes.number,
|
||||
theme: PropTypes.object.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isActive: false,
|
||||
isInfo: false,
|
||||
isUnread: false,
|
||||
size: 12,
|
||||
size: 12
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
isActive,
|
||||
isUnread,
|
||||
isInfo,
|
||||
membersCount,
|
||||
size,
|
||||
status,
|
||||
teammateDeletedAt,
|
||||
theme,
|
||||
type,
|
||||
} = this.props;
|
||||
const {isActive, isUnread, isInfo, membersCount, size, status, theme, type} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
let activeIcon;
|
||||
@@ -106,14 +89,6 @@ export default class ChannelIcon extends React.PureComponent {
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
} else if (type === General.DM_CHANNEL && teammateDeletedAt) {
|
||||
icon = (
|
||||
<ArchiveIcon
|
||||
width={size}
|
||||
height={size}
|
||||
color={offlineColor}
|
||||
/>
|
||||
);
|
||||
} else if (type === General.DM_CHANNEL) {
|
||||
switch (status) {
|
||||
case General.AWAY:
|
||||
@@ -167,49 +142,49 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
marginRight: 12,
|
||||
alignItems: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
icon: {
|
||||
color: changeOpacity(theme.sidebarText, 0.4),
|
||||
color: changeOpacity(theme.sidebarText, 0.4)
|
||||
},
|
||||
iconActive: {
|
||||
color: theme.sidebarTextActiveColor,
|
||||
color: theme.sidebarTextActiveColor
|
||||
},
|
||||
iconUnread: {
|
||||
color: theme.sidebarUnreadText,
|
||||
color: theme.sidebarUnreadText
|
||||
},
|
||||
iconInfo: {
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
groupBox: {
|
||||
alignSelf: 'flex-start',
|
||||
alignItems: 'center',
|
||||
borderWidth: 1,
|
||||
borderColor: changeOpacity(theme.sidebarText, 0.4),
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
groupBoxActive: {
|
||||
borderColor: theme.sidebarTextActiveColor,
|
||||
borderColor: theme.sidebarTextActiveColor
|
||||
},
|
||||
groupBoxUnread: {
|
||||
borderColor: theme.sidebarUnreadText,
|
||||
borderColor: theme.sidebarUnreadText
|
||||
},
|
||||
groupBoxInfo: {
|
||||
borderColor: theme.centerChannelColor,
|
||||
borderColor: theme.centerChannelColor
|
||||
},
|
||||
group: {
|
||||
color: changeOpacity(theme.sidebarText, 0.4),
|
||||
fontSize: 10,
|
||||
fontWeight: '600',
|
||||
fontWeight: '600'
|
||||
},
|
||||
groupActive: {
|
||||
color: theme.sidebarTextActiveColor,
|
||||
color: theme.sidebarTextActiveColor
|
||||
},
|
||||
groupUnread: {
|
||||
color: theme.sidebarUnreadText,
|
||||
color: theme.sidebarUnreadText
|
||||
},
|
||||
groupInfo: {
|
||||
color: theme.centerChannelColor,
|
||||
},
|
||||
color: theme.centerChannelColor
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Platform,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {getFullName} from 'mattermost-redux/utils/user_utils';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
@@ -26,32 +25,27 @@ class ChannelIntro extends PureComponent {
|
||||
intl: intlShape.isRequired,
|
||||
isLoadingPosts: PropTypes.bool,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
goToUserProfile = (userId) => {
|
||||
const {intl, navigator, theme} = this.props;
|
||||
const options = {
|
||||
|
||||
navigator.push({
|
||||
screen: 'UserProfile',
|
||||
title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
userId,
|
||||
userId
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
navigator.push(options);
|
||||
} else {
|
||||
navigator.showModal(options);
|
||||
}
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getDisplayName = (member) => {
|
||||
@@ -75,7 +69,7 @@ class ChannelIntro extends PureComponent {
|
||||
return currentChannelMembers.map((member) => (
|
||||
<TouchableOpacity
|
||||
key={member.id}
|
||||
onPress={preventDoubleTap(() => this.goToUserProfile(member.id))}
|
||||
onPress={() => preventDoubleTap(this.goToUserProfile, this, member.id)}
|
||||
style={style.profile}
|
||||
>
|
||||
<ProfilePicture
|
||||
@@ -95,7 +89,7 @@ class ChannelIntro extends PureComponent {
|
||||
return currentChannelMembers.map((member, index) => (
|
||||
<TouchableOpacity
|
||||
key={member.id}
|
||||
onPress={preventDoubleTap(() => this.goToUserProfile(member.id))}
|
||||
onPress={() => preventDoubleTap(this.goToUserProfile, this, member.id)}
|
||||
>
|
||||
<Text style={style.displayName}>
|
||||
{index === currentChannelMembers.length - 1 ? this.getDisplayName(member) : `${this.getDisplayName(member)}, `}
|
||||
@@ -115,9 +109,9 @@ class ChannelIntro extends PureComponent {
|
||||
<Text style={style.message}>
|
||||
{intl.formatMessage({
|
||||
id: 'mobile.intro_messages.DM',
|
||||
defaultMessage: 'This is the start of your direct message history with {teammate}. Direct messages and files shared here are not shown to people outside this area.',
|
||||
defaultMessage: 'This is the start of your direct message history with {teammate}. Direct messages and files shared here are not shown to people outside this area.'
|
||||
}, {
|
||||
teammate,
|
||||
teammate
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
@@ -134,7 +128,7 @@ class ChannelIntro extends PureComponent {
|
||||
<Text style={style.message}>
|
||||
{intl.formatMessage({
|
||||
id: 'intro_messages.group_message',
|
||||
defaultMessage: 'This is the start of your group message history with these teammates. Messages and files shared here are not shown to people outside this area.',
|
||||
defaultMessage: 'This is the start of your group message history with these teammates. Messages and files shared here are not shown to people outside this area.'
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
@@ -147,7 +141,7 @@ class ChannelIntro extends PureComponent {
|
||||
const date = intl.formatDate(currentChannel.create_at, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
day: 'numeric'
|
||||
});
|
||||
|
||||
let mainMessageIntl;
|
||||
@@ -162,9 +156,9 @@ class ChannelIntro extends PureComponent {
|
||||
date,
|
||||
type: intl.formatMessage({
|
||||
id: 'intro_messages.channel',
|
||||
defaultMessage: 'channel',
|
||||
}),
|
||||
},
|
||||
defaultMessage: 'channel'
|
||||
})
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mainMessageIntl = {
|
||||
@@ -175,20 +169,20 @@ class ChannelIntro extends PureComponent {
|
||||
date,
|
||||
type: intl.formatMessage({
|
||||
id: 'intro_messages.channel',
|
||||
defaultMessage: 'channel',
|
||||
}),
|
||||
},
|
||||
defaultMessage: 'channel'
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const mainMessage = intl.formatMessage({
|
||||
id: mainMessageIntl.id,
|
||||
defaultMessage: mainMessageIntl.defaultMessage,
|
||||
defaultMessage: mainMessageIntl.defaultMessage
|
||||
}, mainMessageIntl.values);
|
||||
|
||||
const anyMemberMessage = intl.formatMessage({
|
||||
id: 'intro_messages.anyMember',
|
||||
defaultMessage: ' Any member can join and read this channel.',
|
||||
defaultMessage: ' Any member can join and read this channel.'
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -196,9 +190,9 @@ class ChannelIntro extends PureComponent {
|
||||
<Text style={style.channelTitle}>
|
||||
{intl.formatMessage({
|
||||
id: 'intro_messages.beginning',
|
||||
defaultMessage: 'Beginning of {name}',
|
||||
defaultMessage: 'Beginning of {name}'
|
||||
}, {
|
||||
name: currentChannel.display_name,
|
||||
name: currentChannel.display_name
|
||||
})}
|
||||
</Text>
|
||||
<Text style={style.message}>
|
||||
@@ -216,25 +210,25 @@ class ChannelIntro extends PureComponent {
|
||||
const date = intl.formatDate(currentChannel.create_at, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
day: 'numeric'
|
||||
});
|
||||
|
||||
const mainMessage = intl.formatMessage({
|
||||
id: 'intro_messages.creator',
|
||||
defaultMessage: 'This is the start of the {name} {type}, created by {creator} on {date}.',
|
||||
defaultMessage: 'This is the start of the {name} {type}, created by {creator} on {date}.'
|
||||
}, {
|
||||
name: currentChannel.display_name,
|
||||
creator: creatorName,
|
||||
date,
|
||||
type: intl.formatMessage({
|
||||
id: 'intro_messages.group',
|
||||
defaultMessage: 'private channel',
|
||||
}),
|
||||
defaultMessage: 'private channel'
|
||||
})
|
||||
});
|
||||
|
||||
const onlyInvitedMessage = intl.formatMessage({
|
||||
id: 'intro_messages.onlyInvited',
|
||||
defaultMessage: ' Only invited members can see this private channel.',
|
||||
defaultMessage: ' Only invited members can see this private channel.'
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -242,9 +236,9 @@ class ChannelIntro extends PureComponent {
|
||||
<Text style={style.channelTitle}>
|
||||
{intl.formatMessage({
|
||||
id: 'intro_messages.beginning',
|
||||
defaultMessage: 'Beginning of {name}',
|
||||
defaultMessage: 'Beginning of {name}'
|
||||
}, {
|
||||
name: currentChannel.display_name,
|
||||
name: currentChannel.display_name
|
||||
})}
|
||||
</Text>
|
||||
<Text style={style.message}>
|
||||
@@ -263,23 +257,23 @@ class ChannelIntro extends PureComponent {
|
||||
<Text style={style.channelTitle}>
|
||||
{intl.formatMessage({
|
||||
id: 'intro_messages.beginning',
|
||||
defaultMessage: 'Beginning of {name}',
|
||||
defaultMessage: 'Beginning of {name}'
|
||||
}, {
|
||||
name: currentChannel.display_name,
|
||||
name: currentChannel.display_name
|
||||
})}
|
||||
</Text>
|
||||
<Text style={style.channelWelcome}>
|
||||
{intl.formatMessage({
|
||||
id: 'mobile.intro_messages.default_welcome',
|
||||
defaultMessage: 'Welcome to {name}!',
|
||||
defaultMessage: 'Welcome to {name}!'
|
||||
}, {
|
||||
name: currentChannel.display_name,
|
||||
name: currentChannel.display_name
|
||||
})}
|
||||
</Text>
|
||||
<Text style={style.message}>
|
||||
{intl.formatMessage({
|
||||
id: 'mobile.intro_messages.default_message',
|
||||
defaultMessage: 'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.',
|
||||
defaultMessage: 'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -354,42 +348,42 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 19,
|
||||
fontWeight: '600',
|
||||
marginBottom: 12,
|
||||
marginBottom: 12
|
||||
},
|
||||
channelWelcome: {
|
||||
color: theme.centerChannelColor,
|
||||
marginBottom: 12,
|
||||
marginBottom: 12
|
||||
},
|
||||
container: {
|
||||
marginTop: 60,
|
||||
marginHorizontal: 12,
|
||||
marginBottom: 12,
|
||||
marginBottom: 12
|
||||
},
|
||||
displayName: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
fontWeight: '600'
|
||||
},
|
||||
message: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.8),
|
||||
fontSize: 15,
|
||||
lineHeight: 22,
|
||||
lineHeight: 22
|
||||
},
|
||||
namesContainer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginBottom: 12,
|
||||
marginBottom: 12
|
||||
},
|
||||
profile: {
|
||||
height: 67,
|
||||
marginBottom: 12,
|
||||
marginRight: 12,
|
||||
marginRight: 12
|
||||
},
|
||||
profilesContainer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
justifyContent: 'flex-start'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -10,14 +10,24 @@ import {getCurrentUserId, getUser, makeGetProfilesInChannel} from 'mattermost-re
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {getChannelMembersForDm} from 'app/selectors/channel';
|
||||
|
||||
import ChannelIntro from './channel_intro';
|
||||
|
||||
function makeMapStateToProps() {
|
||||
const getChannel = makeGetChannel();
|
||||
const getProfilesInChannel = makeGetProfilesInChannel();
|
||||
|
||||
const getOtherUserIdForDm = createSelector(
|
||||
(state, channel) => channel,
|
||||
getCurrentUserId,
|
||||
(channel, currentUserId) => {
|
||||
if (!channel) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return channel.name.split('__').find((m) => m !== currentUserId) || currentUserId;
|
||||
}
|
||||
);
|
||||
|
||||
const getChannelMembers = createSelector(
|
||||
getCurrentUserId,
|
||||
(state, channel) => getProfilesInChannel(state, channel.id),
|
||||
@@ -27,6 +37,17 @@ function makeMapStateToProps() {
|
||||
}
|
||||
);
|
||||
|
||||
const getChannelMembersForDm = createSelector(
|
||||
(state, channel) => getUser(state, getOtherUserIdForDm(state, channel)),
|
||||
(otherUser) => {
|
||||
if (!otherUser) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [otherUser];
|
||||
}
|
||||
);
|
||||
|
||||
return function mapStateToProps(state, ownProps) {
|
||||
const currentChannel = getChannel(state, {id: ownProps.channelId}) || {};
|
||||
const {status: getPostsRequestStatus} = state.requests.posts.getPosts;
|
||||
@@ -51,7 +72,7 @@ function makeMapStateToProps() {
|
||||
currentChannel,
|
||||
currentChannelMembers,
|
||||
isLoadingPosts: (!postsInChannel || postsInChannel.length === 0) && getPostsRequestStatus === RequestStatus.STARTED,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,22 +15,22 @@ export default class ChannelLink extends React.PureComponent {
|
||||
channelsByName: PropTypes.object.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
handleSelectChannel: PropTypes.func.isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
channel: this.getChannelFromChannelName(props),
|
||||
channel: this.getChannelFromChannelName(props)
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.channelName !== this.props.channelName || nextProps.channelsByName !== this.props.channelsByName) {
|
||||
this.setState({
|
||||
channel: this.getChannelFromChannelName(nextProps),
|
||||
channel: this.getChannelFromChannelName(nextProps)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export default class ChannelLink extends React.PureComponent {
|
||||
const channel = this.state.channel;
|
||||
|
||||
if (!channel) {
|
||||
return <Text style={this.props.textStyle}>{`~${this.props.channelName}`}</Text>;
|
||||
return <Text style={this.props.textStyle}>{'~' + this.props.channelName}</Text>;
|
||||
}
|
||||
|
||||
const suffix = this.props.channelName.substring(channel.name.length);
|
||||
@@ -74,7 +74,7 @@ export default class ChannelLink extends React.PureComponent {
|
||||
style={this.props.linkStyle}
|
||||
onPress={this.handlePress}
|
||||
>
|
||||
{`~${channel.display_name}`}
|
||||
{channel.display_name}
|
||||
</Text>
|
||||
{suffix}
|
||||
</Text>
|
||||
|
||||
@@ -12,7 +12,7 @@ import ChannelLink from './channel_link';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
channelsByName: getChannelsNameMapInCurrentTeam(state),
|
||||
channelsByName: getChannelsNameMapInCurrentTeam(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
handleSelectChannel,
|
||||
setChannelDisplayName,
|
||||
}, dispatch),
|
||||
setChannelDisplayName
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Platform,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
|
||||
@@ -19,7 +19,7 @@ export default class ChannelLoader extends PureComponent {
|
||||
static propTypes = {
|
||||
channelIsLoading: PropTypes.bool.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
buildSections(key, style, top) {
|
||||
@@ -36,7 +36,7 @@ export default class ChannelLoader extends PureComponent {
|
||||
colors={[
|
||||
changeOpacity('#e5e5e4', GRADIENT_START),
|
||||
changeOpacity('#d6d6d5', GRADIENT_MIDDLE),
|
||||
changeOpacity('#e5e5e4', GRADIENT_END),
|
||||
changeOpacity('#e5e5e4', GRADIENT_END)
|
||||
]}
|
||||
locations={[0.1, 0.3, 0.7]}
|
||||
style={[style.messageText, {width: 106}]}
|
||||
@@ -47,7 +47,7 @@ export default class ChannelLoader extends PureComponent {
|
||||
colors={[
|
||||
changeOpacity('#e5e5e4', GRADIENT_START),
|
||||
changeOpacity('#d6d6d5', GRADIENT_MIDDLE),
|
||||
changeOpacity('#e5e5e4', GRADIENT_END),
|
||||
changeOpacity('#e5e5e4', GRADIENT_END)
|
||||
]}
|
||||
locations={[0.1, 0.3, 0.7]}
|
||||
style={[style.messageText, {alignSelf: 'stretch'}]}
|
||||
@@ -58,7 +58,7 @@ export default class ChannelLoader extends PureComponent {
|
||||
colors={[
|
||||
changeOpacity('#e5e5e4', GRADIENT_START),
|
||||
changeOpacity('#d6d6d5', GRADIENT_MIDDLE),
|
||||
changeOpacity('#e5e5e4', GRADIENT_END),
|
||||
changeOpacity('#e5e5e4', GRADIENT_END)
|
||||
]}
|
||||
locations={[0.1, 0.3, 0.7]}
|
||||
style={[style.messageText, {alignSelf: 'stretch'}]}
|
||||
@@ -93,35 +93,35 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
position: 'absolute',
|
||||
...Platform.select({
|
||||
android: {
|
||||
top: 0,
|
||||
top: 0
|
||||
},
|
||||
ios: {
|
||||
top: 15,
|
||||
},
|
||||
}),
|
||||
top: 15
|
||||
}
|
||||
})
|
||||
},
|
||||
avatar: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderRadius: 16,
|
||||
height: 32,
|
||||
width: 32,
|
||||
width: 32
|
||||
},
|
||||
messageText: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 10,
|
||||
marginBottom: 10,
|
||||
marginBottom: 10
|
||||
},
|
||||
section: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flexDirection: 'row',
|
||||
paddingLeft: 12,
|
||||
paddingRight: 20,
|
||||
marginVertical: 10,
|
||||
marginVertical: 10
|
||||
},
|
||||
sectionMessage: {
|
||||
marginLeft: 12,
|
||||
flex: 1,
|
||||
},
|
||||
flex: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
channelIsLoading: state.views.channel.loading,
|
||||
deviceWidth,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export default class CheckMark extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Animated,
|
||||
Linking,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
@@ -27,7 +27,7 @@ export default class ClientUpgradeListener extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
logError: PropTypes.func.isRequired,
|
||||
setLastUpgradeCheck: PropTypes.func.isRequired,
|
||||
setLastUpgradeCheck: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
currentVersion: PropTypes.string,
|
||||
downloadLink: PropTypes.string,
|
||||
@@ -37,11 +37,11 @@ export default class ClientUpgradeListener extends PureComponent {
|
||||
latestVersion: PropTypes.string,
|
||||
minVersion: PropTypes.string,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -54,7 +54,7 @@ export default class ClientUpgradeListener extends PureComponent {
|
||||
});
|
||||
|
||||
this.state = {
|
||||
top: new Animated.Value(-100),
|
||||
top: new Animated.Value(-100)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export default class ClientUpgradeListener extends PureComponent {
|
||||
}
|
||||
Animated.timing(this.state.top, {
|
||||
toValue,
|
||||
duration: 300,
|
||||
duration: 300
|
||||
}).start();
|
||||
};
|
||||
|
||||
@@ -128,11 +128,11 @@ export default class ClientUpgradeListener extends PureComponent {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'mobile.client_upgrade.download_error.title',
|
||||
defaultMessage: 'Upgrade Error',
|
||||
defaultMessage: 'Upgrade Error'
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'mobile.client_upgrade.download_error.message',
|
||||
defaultMessage: 'An error occurred while trying to open the download link.',
|
||||
defaultMessage: 'An error occurred while trying to open the download link.'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -152,17 +152,17 @@ export default class ClientUpgradeListener extends PureComponent {
|
||||
navigatorStyle: {
|
||||
navBarHidden: false,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
statusBarHideWithNavBar: false
|
||||
},
|
||||
navigatorButtons: {
|
||||
leftButtons: [{
|
||||
id: 'close-upgrade',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
icon: this.closeButton
|
||||
}]
|
||||
},
|
||||
passProps: {
|
||||
upgradeType: this.state.upgradeType,
|
||||
},
|
||||
upgradeType: this.state.upgradeType
|
||||
}
|
||||
});
|
||||
|
||||
this.toggleUpgradeMessage(false);
|
||||
@@ -226,28 +226,28 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
justifyContent: 'space-around',
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
borderTopWidth: 1,
|
||||
borderTopWidth: 1
|
||||
},
|
||||
button: {
|
||||
color: theme.linkColor,
|
||||
fontSize: 13,
|
||||
paddingHorizontal: 5,
|
||||
paddingVertical: 5,
|
||||
paddingVertical: 5
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelBg, 0.8),
|
||||
borderRadius: 5,
|
||||
borderRadius: 5
|
||||
},
|
||||
message: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
messageText: {
|
||||
fontSize: 16,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.8),
|
||||
fontWeight: '600',
|
||||
fontWeight: '600'
|
||||
},
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
@@ -262,10 +262,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
shadowColor: theme.centerChannelColor,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 3,
|
||||
height: 3
|
||||
},
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 2,
|
||||
},
|
||||
shadowRadius: 2
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ function mapStateToProps(state) {
|
||||
lastUpgradeCheck: state.views.clientUpgrade.lastUpdateCheck,
|
||||
latestVersion,
|
||||
minVersion,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
logError,
|
||||
setLastUpgradeCheck,
|
||||
}, dispatch),
|
||||
setLastUpgradeCheck
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
export default class ConditionalTouchable extends React.PureComponent {
|
||||
static propTypes = {
|
||||
touchable: PropTypes.bool,
|
||||
children: CustomPropTypes.Children.isRequired,
|
||||
children: CustomPropTypes.Children.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
@@ -17,7 +17,7 @@ export default class ChannelListRow extends React.PureComponent {
|
||||
id: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
channel: PropTypes.object.isRequired,
|
||||
...CustomListRow.propTypes,
|
||||
...CustomListRow.propTypes
|
||||
};
|
||||
|
||||
onPress = () => {
|
||||
@@ -70,25 +70,25 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
titleContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
displayName: {
|
||||
fontSize: 16,
|
||||
color: theme.centerChannelColor,
|
||||
marginLeft: 5,
|
||||
marginLeft: 5
|
||||
},
|
||||
icon: {
|
||||
fontSize: 16,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
purpose: {
|
||||
marginTop: 7,
|
||||
fontSize: 13,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
},
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ function makeMapStateToProps() {
|
||||
return (state, ownProps) => {
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
channel: getChannel(state, ownProps),
|
||||
channel: getChannel(state, ownProps)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
@@ -19,11 +19,11 @@ export default class CustomListRow extends React.PureComponent {
|
||||
enabled: PropTypes.bool,
|
||||
selectable: PropTypes.bool,
|
||||
selected: PropTypes.bool,
|
||||
children: CustomPropTypes.Children,
|
||||
children: CustomPropTypes.Children
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
enabled: true,
|
||||
enabled: true
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -62,11 +62,11 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
height: 65,
|
||||
paddingHorizontal: 15,
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
displayName: {
|
||||
fontSize: 15,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
selector: {
|
||||
height: 28,
|
||||
@@ -75,20 +75,20 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
borderWidth: 1,
|
||||
borderColor: '#888',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
selectorContainer: {
|
||||
height: 50,
|
||||
paddingRight: 15,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
selectorDisabled: {
|
||||
backgroundColor: '#888',
|
||||
backgroundColor: '#888'
|
||||
},
|
||||
selectorFilled: {
|
||||
backgroundColor: '#378FD2',
|
||||
borderWidth: 0,
|
||||
},
|
||||
borderWidth: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ export default class CustomList extends PureComponent {
|
||||
onRowSelect: PropTypes.func,
|
||||
renderRow: PropTypes.func.isRequired,
|
||||
createSections: PropTypes.func,
|
||||
showNoResults: PropTypes.bool,
|
||||
showNoResults: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -40,7 +40,7 @@ export default class CustomList extends PureComponent {
|
||||
selectable: false,
|
||||
createSections: () => true,
|
||||
showSections: true,
|
||||
showNoResults: true,
|
||||
showNoResults: true
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -64,7 +64,7 @@ export default class CustomList extends PureComponent {
|
||||
const dataSource = showSections ? this.state.dataSource.cloneWithRowsAndSections(mergedData) : this.state.dataSource.cloneWithRows(mergedData);
|
||||
this.setState({
|
||||
data: mergedData,
|
||||
dataSource,
|
||||
dataSource
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ export default class CustomList extends PureComponent {
|
||||
buildDataSource = (props) => {
|
||||
const ds = new ListView.DataSource({
|
||||
rowHasChanged: (r1, r2) => r1 !== r2,
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
|
||||
});
|
||||
let newData = props.data;
|
||||
if (props.showSections) {
|
||||
@@ -81,7 +81,7 @@ export default class CustomList extends PureComponent {
|
||||
const dataSource = props.showSections ? ds.cloneWithRowsAndSections(newData) : ds.cloneWithRows(newData);
|
||||
return {
|
||||
data: newData,
|
||||
dataSource,
|
||||
dataSource
|
||||
};
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ export default class CustomList extends PureComponent {
|
||||
const dataSource = this.state.dataSource.cloneWithRowsAndSections(mergedData);
|
||||
this.setState({
|
||||
data: mergedData,
|
||||
dataSource,
|
||||
dataSource
|
||||
}, () => this.props.onRowSelect(id));
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ export default class CustomList extends PureComponent {
|
||||
item,
|
||||
selected: item.selected,
|
||||
selectable: this.props.selectable,
|
||||
onPress: this.props.onRowPress,
|
||||
onPress: this.props.onRowPress
|
||||
};
|
||||
|
||||
if ('disableSelect' in item) {
|
||||
@@ -181,7 +181,7 @@ export default class CustomList extends PureComponent {
|
||||
searching,
|
||||
showNoResults,
|
||||
showSections,
|
||||
theme,
|
||||
theme
|
||||
} = this.props;
|
||||
const {dataSource} = this.state;
|
||||
const style = getStyleFromTheme(theme);
|
||||
@@ -245,45 +245,45 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
}),
|
||||
marginBottom: 20
|
||||
}
|
||||
})
|
||||
},
|
||||
loadingText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6),
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6)
|
||||
},
|
||||
searching: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
width: '100%'
|
||||
},
|
||||
sectionContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.07),
|
||||
paddingLeft: 10,
|
||||
paddingVertical: 2,
|
||||
paddingVertical: 2
|
||||
},
|
||||
sectionWrapper: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
sectionText: {
|
||||
fontWeight: '600',
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
separator: {
|
||||
height: 1,
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
noResultContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
noResultText: {
|
||||
fontSize: 26,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
},
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ function mapStateToProps(state, ownProps) {
|
||||
isMyUser: getCurrentUserId(state) === ownProps.id,
|
||||
theme: getTheme(state),
|
||||
user: getUser(state, ownProps.id),
|
||||
teammateNameDisplay: getTeammateNameDisplaySetting(state),
|
||||
teammateNameDisplay: getTeammateNameDisplaySetting(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
import {
|
||||
Text,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
@@ -22,11 +22,11 @@ export default class UserListRow extends React.PureComponent {
|
||||
theme: PropTypes.object.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
teammateNameDisplay: PropTypes.string.isRequired,
|
||||
...CustomListRow.propTypes,
|
||||
...CustomListRow.propTypes
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
onPress = () => {
|
||||
@@ -44,7 +44,7 @@ export default class UserListRow extends React.PureComponent {
|
||||
selected,
|
||||
teammateNameDisplay,
|
||||
theme,
|
||||
user,
|
||||
user
|
||||
} = this.props;
|
||||
|
||||
const {id, username} = user;
|
||||
@@ -54,7 +54,7 @@ export default class UserListRow extends React.PureComponent {
|
||||
if (isMyUser) {
|
||||
usernameDisplay = formatMessage({
|
||||
id: 'mobile.more_dms.you',
|
||||
defaultMessage: '(@{username} - you)',
|
||||
defaultMessage: '(@{username} - you)'
|
||||
}, {username});
|
||||
}
|
||||
|
||||
@@ -99,24 +99,24 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
height: 65,
|
||||
paddingHorizontal: 15,
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
displayName: {
|
||||
fontSize: 15,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
icon: {
|
||||
fontSize: 20,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
textContainer: {
|
||||
flexDirection: 'row',
|
||||
marginLeft: 5,
|
||||
marginLeft: 5
|
||||
},
|
||||
username: {
|
||||
marginLeft: 5,
|
||||
fontSize: 15,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
selector: {
|
||||
height: 28,
|
||||
@@ -125,20 +125,20 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
borderWidth: 1,
|
||||
borderColor: '#888',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
selectorContainer: {
|
||||
height: 50,
|
||||
paddingRight: 15,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
selectorDisabled: {
|
||||
backgroundColor: '#888',
|
||||
backgroundColor: '#888'
|
||||
},
|
||||
selectorFilled: {
|
||||
backgroundColor: '#378FD2',
|
||||
borderWidth: 0,
|
||||
},
|
||||
borderWidth: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Platform,
|
||||
SectionList,
|
||||
Text,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import Loading from 'app/components/loading';
|
||||
@@ -85,7 +85,7 @@ export default class CustomSectionList extends React.PureComponent {
|
||||
/*
|
||||
* How many items to render when the list is first rendered.
|
||||
*/
|
||||
initialNumToRender: PropTypes.number,
|
||||
initialNumToRender: PropTypes.number
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -96,21 +96,21 @@ export default class CustomSectionList extends React.PureComponent {
|
||||
onListEndReached: () => true,
|
||||
onListEndReachedThreshold: 50,
|
||||
loadingText: null,
|
||||
initialNumToRender: 10,
|
||||
initialNumToRender: 10
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sections: this.extractSections(props.items),
|
||||
sections: this.extractSections(props.items)
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.items !== this.props.items) {
|
||||
this.setState({
|
||||
sections: this.extractSections(nextProps.items),
|
||||
sections: this.extractSections(nextProps.items)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ export default class CustomSectionList extends React.PureComponent {
|
||||
return sectionKeys.map((sectionKey) => {
|
||||
return {
|
||||
key: sectionKey,
|
||||
data: sections[sectionKey].sort(this.props.compareItems),
|
||||
data: sections[sectionKey].sort(this.props.compareItems)
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -156,7 +156,7 @@ export default class CustomSectionList extends React.PureComponent {
|
||||
const props = {
|
||||
id: item.id,
|
||||
item,
|
||||
onPress: this.props.onRowPress,
|
||||
onPress: this.props.onRowPress
|
||||
};
|
||||
|
||||
// Allow passing in a component like UserListRow or ChannelListRow
|
||||
@@ -258,51 +258,51 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
}),
|
||||
marginBottom: 20
|
||||
}
|
||||
})
|
||||
},
|
||||
loading: {
|
||||
height: 70,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
loadingText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6),
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6)
|
||||
},
|
||||
searching: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
width: '100%'
|
||||
},
|
||||
sectionContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.07),
|
||||
paddingLeft: 10,
|
||||
paddingVertical: 2,
|
||||
paddingVertical: 2
|
||||
},
|
||||
sectionWrapper: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
sectionText: {
|
||||
fontWeight: '600',
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
separator: {
|
||||
height: 1,
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
noResultContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
noResultText: {
|
||||
fontSize: 26,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
},
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ export default class Drawer extends BaseDrawer {
|
||||
...BaseDrawer.propTypes,
|
||||
onRequestClose: PropTypes.func.isRequired,
|
||||
bottomPanOffset: PropTypes.number,
|
||||
topPanOffset: PropTypes.number,
|
||||
topPanOffset: PropTypes.number
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
Text,
|
||||
findNodeHandle,
|
||||
findNodeHandle
|
||||
} from 'react-native';
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||||
|
||||
@@ -47,11 +47,11 @@ export default class EditChannelInfo extends PureComponent {
|
||||
oldDisplayName: PropTypes.string,
|
||||
oldChannelURL: PropTypes.string,
|
||||
oldHeader: PropTypes.string,
|
||||
oldPurpose: PropTypes.string,
|
||||
oldPurpose: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
editing: false,
|
||||
editing: false
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
@@ -96,7 +96,7 @@ export default class EditChannelInfo extends PureComponent {
|
||||
this.props.navigator.pop({animated: true});
|
||||
} else {
|
||||
this.props.navigator.dismissModal({
|
||||
animationType: 'slide-down',
|
||||
animationType: 'slide-down'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -110,7 +110,7 @@ export default class EditChannelInfo extends PureComponent {
|
||||
oldDisplayName,
|
||||
oldChannelURL,
|
||||
oldPurpose,
|
||||
oldHeader,
|
||||
oldHeader
|
||||
} = this.props;
|
||||
|
||||
return displayName !== oldDisplayName || channelURL !== oldChannelURL ||
|
||||
@@ -190,7 +190,7 @@ export default class EditChannelInfo extends PureComponent {
|
||||
displayName,
|
||||
channelURL,
|
||||
header,
|
||||
purpose,
|
||||
purpose
|
||||
} = this.props;
|
||||
const {error, saving} = this.props;
|
||||
const fullUrl = currentTeamUrl + '/channels';
|
||||
@@ -213,7 +213,7 @@ export default class EditChannelInfo extends PureComponent {
|
||||
let displayError;
|
||||
if (error) {
|
||||
displayError = (
|
||||
<View style={[style.errorContainer, {width: deviceWidth}]}>
|
||||
<View style={[style.errorContainer, {deviceWidth}]}>
|
||||
<View style={style.errorWrapper}>
|
||||
<ErrorText error={error}/>
|
||||
</View>
|
||||
@@ -374,54 +374,54 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
paddingTop: 10,
|
||||
paddingTop: 10
|
||||
},
|
||||
errorContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03)
|
||||
},
|
||||
errorWrapper: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
inputContainer: {
|
||||
marginTop: 10,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: '#fff'
|
||||
},
|
||||
input: {
|
||||
color: '#333',
|
||||
fontSize: 14,
|
||||
height: 40,
|
||||
paddingHorizontal: 15,
|
||||
paddingHorizontal: 15
|
||||
},
|
||||
titleContainer30: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 30,
|
||||
marginTop: 30
|
||||
},
|
||||
titleContainer15: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 15,
|
||||
marginTop: 15
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: theme.centerChannelColor,
|
||||
marginLeft: 15,
|
||||
marginLeft: 15
|
||||
},
|
||||
optional: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 14,
|
||||
marginLeft: 5,
|
||||
marginLeft: 5
|
||||
},
|
||||
helpText: {
|
||||
fontSize: 14,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
marginTop: 10,
|
||||
marginHorizontal: 15,
|
||||
},
|
||||
marginHorizontal: 15
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -3,86 +3,60 @@
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Image,
|
||||
PixelRatio,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import {Image, Platform, Text} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
import {EmojiIndicesByAlias, Emojis} from 'app/utils/emojis';
|
||||
|
||||
const scaleEmojiBasedOnDevice = (size) => {
|
||||
if (Platform.OS === 'ios') {
|
||||
return size * 1.1; // slightly larger emojis look better on ios
|
||||
}
|
||||
return size * PixelRatio.get();
|
||||
};
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
export default class Emoji extends React.PureComponent {
|
||||
static propTypes = {
|
||||
|
||||
/*
|
||||
* Emoji text name.
|
||||
*/
|
||||
customEmojis: PropTypes.object,
|
||||
emojiName: PropTypes.string.isRequired,
|
||||
|
||||
/*
|
||||
* Image URL for the emoji.
|
||||
*/
|
||||
imageUrl: PropTypes.string.isRequired,
|
||||
|
||||
/*
|
||||
* Set if this is a custom emoji.
|
||||
*/
|
||||
isCustomEmoji: PropTypes.bool.isRequired,
|
||||
|
||||
/*
|
||||
* Set to render only the text and no image.
|
||||
*/
|
||||
displayTextOnly: PropTypes.bool,
|
||||
fontSize: PropTypes.number,
|
||||
literal: PropTypes.string,
|
||||
size: PropTypes.number,
|
||||
size: PropTypes.number.isRequired,
|
||||
textStyle: CustomPropTypes.Style,
|
||||
token: PropTypes.string.isRequired,
|
||||
token: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
customEmojis: new Map(),
|
||||
literal: '',
|
||||
imageUrl: '',
|
||||
isCustomEmoji: false,
|
||||
literal: ''
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
...this.getImageUrl(props),
|
||||
originalWidth: 0,
|
||||
originalHeight: 0,
|
||||
originalHeight: 0
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.mounted = true;
|
||||
if (!this.props.displayTextOnly && this.props.imageUrl && this.props.isCustomEmoji) {
|
||||
this.updateImageHeight(this.props.imageUrl);
|
||||
if (this.state.imageUrl && this.state.isCustomEmoji) {
|
||||
this.updateImageHeight(this.state.imageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.emojiName !== this.props.emojiName) {
|
||||
if (nextProps.customEmojis !== this.props.customEmojis || nextProps.emojiName !== this.props.emojiName) {
|
||||
this.setState({
|
||||
...this.getImageUrl(nextProps),
|
||||
originalWidth: 0,
|
||||
originalHeight: 0,
|
||||
originalHeight: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextProps.displayTextOnly && nextProps.imageUrl && nextProps.isCustomEmoji &&
|
||||
nextProps.imageUrl !== this.props.imageUrl) {
|
||||
this.updateImageHeight(nextProps.imageUrl);
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
if (nextState.imageUrl !== this.state.imageUrl && nextState.imageUrl && nextState.isCustomEmoji) {
|
||||
this.updateImageHeight(nextState.imageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,12 +64,34 @@ export default class Emoji extends React.PureComponent {
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
getImageUrl = (props = this.props) => {
|
||||
const emojiName = props.emojiName;
|
||||
|
||||
let imageUrl = '';
|
||||
let isCustomEmoji = false;
|
||||
if (EmojiIndicesByAlias.has(emojiName)) {
|
||||
const emoji = Emojis[EmojiIndicesByAlias.get(emojiName)];
|
||||
|
||||
imageUrl = Client4.getSystemEmojiImageUrl(emoji.filename);
|
||||
} else if (props.customEmojis.has(emojiName)) {
|
||||
const emoji = props.customEmojis.get(emojiName);
|
||||
|
||||
imageUrl = Client4.getCustomEmojiImageUrl(emoji.id);
|
||||
isCustomEmoji = true;
|
||||
}
|
||||
|
||||
return {
|
||||
imageUrl,
|
||||
isCustomEmoji
|
||||
};
|
||||
}
|
||||
|
||||
updateImageHeight = (imageUrl) => {
|
||||
Image.getSize(imageUrl, (originalWidth, originalHeight) => {
|
||||
if (this.mounted) {
|
||||
this.setState({
|
||||
originalWidth,
|
||||
originalHeight,
|
||||
originalHeight
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -103,60 +99,17 @@ export default class Emoji extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
fontSize,
|
||||
literal,
|
||||
size,
|
||||
textStyle,
|
||||
token,
|
||||
imageUrl,
|
||||
displayTextOnly,
|
||||
token
|
||||
} = this.props;
|
||||
|
||||
let size = this.props.size;
|
||||
let fontSize = size;
|
||||
if (!size && textStyle) {
|
||||
const flatten = StyleSheet.flatten(textStyle);
|
||||
fontSize = flatten.fontSize;
|
||||
size = scaleEmojiBasedOnDevice(fontSize);
|
||||
}
|
||||
|
||||
if (displayTextOnly) {
|
||||
if (!this.state.imageUrl) {
|
||||
return <Text style={textStyle}>{literal}</Text>;
|
||||
}
|
||||
|
||||
const source = {
|
||||
uri: imageUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
};
|
||||
|
||||
let width = size;
|
||||
let height = size;
|
||||
let {originalHeight, originalWidth} = this.state;
|
||||
originalHeight = scaleEmojiBasedOnDevice(originalHeight);
|
||||
originalWidth = scaleEmojiBasedOnDevice(originalWidth);
|
||||
if (originalHeight && originalWidth) {
|
||||
if (originalWidth > originalHeight) {
|
||||
height = (size * originalHeight) / originalWidth;
|
||||
} else if (originalWidth < originalHeight) {
|
||||
// This may cause text to reflow, but its impossible to add a horizontal margin
|
||||
width = (size * originalWidth) / originalHeight;
|
||||
}
|
||||
}
|
||||
|
||||
let marginTop = 0;
|
||||
if (textStyle) {
|
||||
// hack to get the vertical alignment looking better
|
||||
if (fontSize > 16) {
|
||||
marginTop -= 2;
|
||||
} else if (fontSize <= 16) {
|
||||
marginTop += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Android can't change the size of an image after its first render, so
|
||||
// force a new image to be rendered when the size changes
|
||||
const key = Platform.OS === 'android' ? (height + '-' + width) : null;
|
||||
|
||||
let ImageComponent;
|
||||
if (Platform.OS === 'android') {
|
||||
ImageComponent = Image;
|
||||
@@ -164,15 +117,41 @@ export default class Emoji extends React.PureComponent {
|
||||
ImageComponent = FastImage;
|
||||
}
|
||||
|
||||
if (!imageUrl) {
|
||||
return (
|
||||
<ImageComponent
|
||||
key={key}
|
||||
style={{width, height, marginTop}}
|
||||
/>
|
||||
);
|
||||
const source = {
|
||||
uri: this.state.imageUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
};
|
||||
|
||||
let width = size;
|
||||
let height = size;
|
||||
if (this.state.originalHeight && this.state.originalWidth) {
|
||||
if (this.state.originalWidth > this.state.originalHeight) {
|
||||
height = (size * this.state.originalHeight) / this.state.originalWidth;
|
||||
} else if (this.state.originalWidth < this.state.originalHeight) {
|
||||
// This may cause text to reflow, but its impossible to add a horizontal margin
|
||||
width = (size * this.state.originalWidth) / this.state.originalHeight;
|
||||
}
|
||||
}
|
||||
|
||||
let marginTop = 0;
|
||||
if (fontSize) {
|
||||
// Center the image vertically on iOS (does nothing on Android)
|
||||
marginTop = (height - 16) / 2;
|
||||
|
||||
// hack to get the vertical alignment looking better
|
||||
if (fontSize === 17) {
|
||||
marginTop -= 2;
|
||||
} else if (fontSize === 15) {
|
||||
marginTop += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Android can't change the size of an image after its first render, so
|
||||
// force a new image to be rendered when the size changes
|
||||
const key = Platform.OS === 'android' ? (height + '-' + width) : null;
|
||||
|
||||
return (
|
||||
<ImageComponent
|
||||
key={key}
|
||||
|
||||
@@ -4,41 +4,13 @@
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
|
||||
|
||||
import {EmojiIndicesByAlias, Emojis} from 'app/utils/emojis';
|
||||
|
||||
import Emoji from './emoji';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const emojiName = ownProps.emojiName;
|
||||
const customEmojis = getCustomEmojisByName(state);
|
||||
|
||||
let imageUrl = '';
|
||||
let isCustomEmoji = false;
|
||||
let displayTextOnly = false;
|
||||
if (EmojiIndicesByAlias.has(emojiName)) {
|
||||
const emoji = Emojis[EmojiIndicesByAlias.get(emojiName)];
|
||||
imageUrl = Client4.getSystemEmojiImageUrl(emoji.filename);
|
||||
} else if (customEmojis.has(emojiName)) {
|
||||
const emoji = customEmojis.get(emojiName);
|
||||
imageUrl = Client4.getCustomEmojiImageUrl(emoji.id);
|
||||
isCustomEmoji = true;
|
||||
} else {
|
||||
displayTextOnly = state.entities.emojis.nonExistentEmoji.has(emojiName) ||
|
||||
getConfig(state).EnableCustomEmoji !== 'true' ||
|
||||
getCurrentUserId(state) === '' ||
|
||||
!isMinimumServerVersion(Client4.getServerVersion(), 4, 7);
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
imageUrl,
|
||||
isCustomEmoji,
|
||||
displayTextOnly,
|
||||
token: state.entities.general.credentials.token,
|
||||
customEmojis: getCustomEmojisByName(state),
|
||||
token: state.entities.general.credentials.token
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,21 +5,18 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
SectionList,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
import sectionListGetItemLayout from 'react-native-section-list-get-item-layout';
|
||||
|
||||
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
|
||||
|
||||
import Emoji from 'app/components/emoji';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import SafeAreaView from 'app/components/safe_area_view';
|
||||
@@ -33,33 +30,24 @@ const EMOJI_SIZE = 30;
|
||||
const EMOJI_GUTTER = 7.5;
|
||||
const SECTION_MARGIN = 15;
|
||||
const SECTION_HEADER_HEIGHT = 28;
|
||||
const EMOJIS_PER_PAGE = 200;
|
||||
|
||||
export default class EmojiPicker extends PureComponent {
|
||||
static propTypes = {
|
||||
customEmojisEnabled: PropTypes.bool.isRequired,
|
||||
customEmojiPage: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
fuse: PropTypes.object.isRequired,
|
||||
emojis: PropTypes.array.isRequired,
|
||||
emojisBySection: PropTypes.array.isRequired,
|
||||
fuse: PropTypes.object.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
onEmojiPress: PropTypes.func,
|
||||
serverVersion: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
getCustomEmojis: PropTypes.func.isRequired,
|
||||
incrementEmojiPickerPage: PropTypes.func.isRequired,
|
||||
searchCustomEmojis: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onEmojiPress: emptyFunction,
|
||||
onEmojiPress: emptyFunction
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -69,7 +57,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
getItemHeight: () => {
|
||||
return EMOJI_SIZE + (EMOJI_GUTTER * 2);
|
||||
},
|
||||
getSectionHeaderHeight: () => SECTION_HEADER_HEIGHT,
|
||||
getSectionHeaderHeight: () => SECTION_HEADER_HEIGHT
|
||||
});
|
||||
|
||||
const emojis = this.renderableEmojis(props.emojisBySection, props.deviceWidth);
|
||||
@@ -82,31 +70,20 @@ export default class EmojiPicker extends PureComponent {
|
||||
emojiSectionIndexByOffset,
|
||||
filteredEmojis: [],
|
||||
searchTerm: '',
|
||||
currentSectionIndex: 0,
|
||||
missingPages: isMinimumServerVersion(this.props.serverVersion, 4, 7),
|
||||
currentSectionIndex: 0
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
let rebuildEmojis = false;
|
||||
if (this.props.deviceWidth !== nextProps.deviceWidth) {
|
||||
rebuildEmojis = true;
|
||||
this.setState({
|
||||
emojis: this.renderableEmojis(this.props.emojisBySection, nextProps.deviceWidth)
|
||||
});
|
||||
|
||||
if (this.refs.search_bar) {
|
||||
this.refs.search_bar.blur();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.emojis !== nextProps.emojis) {
|
||||
rebuildEmojis = true;
|
||||
}
|
||||
|
||||
if (rebuildEmojis) {
|
||||
const emojis = this.renderableEmojis(this.props.emojisBySection, nextProps.deviceWidth);
|
||||
this.setState({
|
||||
emojis,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderableEmojis = (emojis, deviceWidth) => {
|
||||
@@ -116,7 +93,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
const data = [];
|
||||
let row = {
|
||||
key: `${section.key}-0`,
|
||||
items: [],
|
||||
items: []
|
||||
};
|
||||
|
||||
section.data.forEach((emoji, index) => {
|
||||
@@ -124,7 +101,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
data.push(row);
|
||||
row = {
|
||||
key: `${section.key}-${index}`,
|
||||
items: [],
|
||||
items: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,7 +120,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
|
||||
return {
|
||||
...section,
|
||||
data,
|
||||
data
|
||||
};
|
||||
});
|
||||
|
||||
@@ -162,34 +139,24 @@ export default class EmojiPicker extends PureComponent {
|
||||
};
|
||||
|
||||
changeSearchTerm = (text) => {
|
||||
const nextState = {
|
||||
searchTerm: text,
|
||||
};
|
||||
|
||||
if (!text) {
|
||||
nextState.currentSectionIndex = 0;
|
||||
}
|
||||
|
||||
this.setState(nextState);
|
||||
this.setState({
|
||||
searchTerm: text
|
||||
});
|
||||
|
||||
clearTimeout(this.searchTermTimeout);
|
||||
const timeout = text ? 100 : 0;
|
||||
this.searchTermTimeout = setTimeout(async () => {
|
||||
if (isMinimumServerVersion(this.props.serverVersion, 4, 7)) {
|
||||
await this.props.actions.searchCustomEmojis(text);
|
||||
}
|
||||
this.searchTermTimeout = setTimeout(() => {
|
||||
const filteredEmojis = this.searchEmojis(text);
|
||||
this.setState({
|
||||
filteredEmojis,
|
||||
filteredEmojis
|
||||
});
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
cancelSearch = () => {
|
||||
this.setState({
|
||||
currentSectionIndex: 0,
|
||||
filteredEmojis: [],
|
||||
searchTerm: '',
|
||||
searchTerm: ''
|
||||
});
|
||||
};
|
||||
|
||||
@@ -247,26 +214,6 @@ export default class EmojiPicker extends PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
loadMoreCustomEmojis = async () => {
|
||||
if (!this.props.customEmojisEnabled || !isMinimumServerVersion(this.props.serverVersion, 4, 7)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {data} = await this.props.actions.getCustomEmojis(this.props.customEmojiPage, EMOJIS_PER_PAGE);
|
||||
this.setState({loadingMore: false});
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length < EMOJIS_PER_PAGE) {
|
||||
this.setState({missingPages: false});
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.actions.incrementEmojiPickerPage();
|
||||
}
|
||||
|
||||
onScroll = (e) => {
|
||||
if (this.state.jumpToSection) {
|
||||
return;
|
||||
@@ -285,7 +232,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
|
||||
if (nextIndex !== this.state.currentSectionIndex) {
|
||||
this.setState({
|
||||
currentSectionIndex: nextIndex,
|
||||
currentSectionIndex: nextIndex
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -293,7 +240,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
onMomentumScrollEnd = () => {
|
||||
if (this.state.jumpToSection) {
|
||||
this.setState({
|
||||
jumpToSection: false,
|
||||
jumpToSection: false
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -301,12 +248,12 @@ export default class EmojiPicker extends PureComponent {
|
||||
scrollToSection = (index) => {
|
||||
this.setState({
|
||||
jumpToSection: true,
|
||||
currentSectionIndex: index,
|
||||
currentSectionIndex: index
|
||||
}, () => {
|
||||
this.sectionList.scrollToLocation({
|
||||
sectionIndex: index,
|
||||
itemIndex: 0,
|
||||
viewOffset: 25,
|
||||
viewOffset: 25
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -338,13 +285,9 @@ export default class EmojiPicker extends PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
handleSectionIconPress = (index, isCustomSection = false) => {
|
||||
handleSectionIconPress = (index) => {
|
||||
this.scrollToSectionTries = 0;
|
||||
this.scrollToSection(index);
|
||||
|
||||
if (isCustomSection && this.props.customEmojiPage === 0) {
|
||||
this.loadMoreCustomEmojis();
|
||||
}
|
||||
}
|
||||
|
||||
renderSectionIcons = () => {
|
||||
@@ -352,7 +295,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
const styles = getStyleSheetFromTheme(theme);
|
||||
|
||||
return this.state.emojis.map((section, index) => {
|
||||
const onPress = () => this.handleSectionIconPress(index, section.key === 'custom');
|
||||
const onPress = () => this.handleSectionIconPress(index);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
@@ -374,21 +317,6 @@ export default class EmojiPicker extends PureComponent {
|
||||
this.sectionList = c;
|
||||
};
|
||||
|
||||
renderFooter = () => {
|
||||
if (!this.state.missingPages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {theme} = this.props;
|
||||
|
||||
const styles = getStyleSheetFromTheme(theme);
|
||||
return (
|
||||
<View style={styles.loading}>
|
||||
<ActivityIndicator/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {deviceWidth, isLandscape, theme} = this.props;
|
||||
const {emojis, filteredEmojis, searchTerm} = this.state;
|
||||
@@ -425,9 +353,6 @@ export default class EmojiPicker extends PureComponent {
|
||||
onScrollToIndexFailed={this.handleScrollToSectionFailed}
|
||||
onMomentumScrollEnd={this.onMomentumScrollEnd}
|
||||
pageSize={30}
|
||||
ListFooterComponent={this.renderFooter}
|
||||
onEndReached={this.loadMoreCustomEmojis}
|
||||
onEndReachedThreshold={Platform.OS === 'ios' ? 0 : 1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -441,17 +366,6 @@ export default class EmojiPicker extends PureComponent {
|
||||
keyboardOffset = 52;
|
||||
}
|
||||
|
||||
const searchBarInput = {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 13,
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: -3,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView excludeHeader={true}>
|
||||
<KeyboardAvoidingView
|
||||
@@ -466,7 +380,11 @@ export default class EmojiPicker extends PureComponent {
|
||||
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
|
||||
backgroundColor='transparent'
|
||||
inputHeight={33}
|
||||
inputStyle={searchBarInput}
|
||||
inputStyle={{
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 13
|
||||
}}
|
||||
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.5)}
|
||||
tintColorSearch={changeOpacity(theme.centerChannelColor, 0.8)}
|
||||
tintColorDelete={changeOpacity(theme.centerChannelColor, 0.5)}
|
||||
@@ -499,7 +417,7 @@ const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.3),
|
||||
borderTopWidth: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
bottomContentWrapper: {
|
||||
position: 'absolute',
|
||||
@@ -507,43 +425,43 @@ const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 35,
|
||||
width: '100%',
|
||||
width: '100%'
|
||||
},
|
||||
columnStyle: {
|
||||
alignSelf: 'stretch',
|
||||
flexDirection: 'row',
|
||||
marginVertical: EMOJI_GUTTER,
|
||||
justifyContent: 'flex-start',
|
||||
justifyContent: 'flex-start'
|
||||
},
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flex: 1,
|
||||
flex: 1
|
||||
},
|
||||
emoji: {
|
||||
width: EMOJI_SIZE,
|
||||
height: EMOJI_SIZE,
|
||||
marginHorizontal: EMOJI_GUTTER,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
emojiLeft: {
|
||||
marginLeft: 0,
|
||||
marginLeft: 0
|
||||
},
|
||||
emojiRight: {
|
||||
marginRight: 0,
|
||||
marginRight: 0
|
||||
},
|
||||
flatList: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
alignSelf: 'stretch',
|
||||
alignSelf: 'stretch'
|
||||
},
|
||||
flatListEmoji: {
|
||||
marginRight: 5,
|
||||
marginRight: 5
|
||||
},
|
||||
flatListEmojiName: {
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
flatListRow: {
|
||||
height: 40,
|
||||
@@ -556,47 +474,43 @@ const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
borderLeftWidth: 1,
|
||||
borderLeftColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderRightColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
},
|
||||
listView: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
marginBottom: 35,
|
||||
marginBottom: 35
|
||||
},
|
||||
searchBar: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
paddingVertical: 5,
|
||||
paddingVertical: 5
|
||||
},
|
||||
section: {
|
||||
alignItems: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
sectionIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.3),
|
||||
color: changeOpacity(theme.centerChannelColor, 0.3)
|
||||
},
|
||||
sectionIconContainer: {
|
||||
flex: 1,
|
||||
height: 35,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
sectionIconHighlight: {
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
sectionTitle: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
fontWeight: '700'
|
||||
},
|
||||
sectionTitleContainer: {
|
||||
height: SECTION_HEADER_HEIGHT,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
},
|
||||
loading: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
},
|
||||
flex: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,9 +6,8 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import shallowEqual from 'shallow-equals';
|
||||
|
||||
import Emoji from 'app/components/emoji';
|
||||
|
||||
@@ -17,11 +16,11 @@ export default class EmojiPickerRow extends Component {
|
||||
emojiGutter: PropTypes.number.isRequired,
|
||||
emojiSize: PropTypes.number.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
onEmojiPress: PropTypes.func.isRequired,
|
||||
onEmojiPress: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !shallowEqual(this.props.items, nextProps.items);
|
||||
return this.props.items.length !== nextProps.items.length;
|
||||
}
|
||||
|
||||
renderEmojis = (emoji, index, emojis) => {
|
||||
@@ -32,8 +31,8 @@ export default class EmojiPickerRow extends Component {
|
||||
{
|
||||
width: emojiSize,
|
||||
height: emojiSize,
|
||||
marginHorizontal: emojiGutter,
|
||||
},
|
||||
marginHorizontal: emojiGutter
|
||||
}
|
||||
];
|
||||
if (index === 0) {
|
||||
style.push(styles.emojiLeft);
|
||||
@@ -81,16 +80,16 @@ const styles = StyleSheet.create({
|
||||
columnStyle: {
|
||||
alignSelf: 'stretch',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
emoji: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
emojiLeft: {
|
||||
marginLeft: 0,
|
||||
marginLeft: 0
|
||||
},
|
||||
emojiRight: {
|
||||
marginRight: 0,
|
||||
},
|
||||
});
|
||||
marginRight: 0
|
||||
}
|
||||
});
|
||||
@@ -3,16 +3,11 @@
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {createSelector} from 'reselect';
|
||||
import {bindActionCreators} from 'redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCustomEmojis, searchCustomEmojis} from 'mattermost-redux/actions/emojis';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import {incrementEmojiPickerPage} from 'app/actions/views/emoji';
|
||||
import {getDimensions, isLandscape} from 'app/selectors/device';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {CategoryNames, Emojis, EmojiIndicesByAlias, EmojiIndicesByCategory} from 'app/utils/emojis';
|
||||
|
||||
import EmojiPicker from './emoji_picker';
|
||||
@@ -22,60 +17,60 @@ const categoryToI18n = {
|
||||
activity: {
|
||||
id: 'mobile.emoji_picker.activity',
|
||||
defaultMessage: 'ACTIVITY',
|
||||
icon: 'futbol-o',
|
||||
icon: 'futbol-o'
|
||||
},
|
||||
custom: {
|
||||
id: 'mobile.emoji_picker.custom',
|
||||
defaultMessage: 'CUSTOM',
|
||||
icon: 'at',
|
||||
icon: 'at'
|
||||
},
|
||||
flags: {
|
||||
id: 'mobile.emoji_picker.flags',
|
||||
defaultMessage: 'FLAGS',
|
||||
icon: 'flag-o',
|
||||
icon: 'flag-o'
|
||||
},
|
||||
foods: {
|
||||
id: 'mobile.emoji_picker.foods',
|
||||
defaultMessage: 'FOODS',
|
||||
icon: 'cutlery',
|
||||
icon: 'cutlery'
|
||||
},
|
||||
nature: {
|
||||
id: 'mobile.emoji_picker.nature',
|
||||
defaultMessage: 'NATURE',
|
||||
icon: 'leaf',
|
||||
icon: 'leaf'
|
||||
},
|
||||
objects: {
|
||||
id: 'mobile.emoji_picker.objects',
|
||||
defaultMessage: 'OBJECTS',
|
||||
icon: 'lightbulb-o',
|
||||
icon: 'lightbulb-o'
|
||||
},
|
||||
people: {
|
||||
id: 'mobile.emoji_picker.people',
|
||||
defaultMessage: 'PEOPLE',
|
||||
icon: 'smile-o',
|
||||
icon: 'smile-o'
|
||||
},
|
||||
places: {
|
||||
id: 'mobile.emoji_picker.places',
|
||||
defaultMessage: 'PLACES',
|
||||
icon: 'plane',
|
||||
icon: 'plane'
|
||||
},
|
||||
recent: {
|
||||
id: 'mobile.emoji_picker.recent',
|
||||
defaultMessage: 'RECENTLY USED',
|
||||
icon: 'clock-o',
|
||||
icon: 'clock-o'
|
||||
},
|
||||
symbols: {
|
||||
id: 'mobile.emoji_picker.symbols',
|
||||
defaultMessage: 'SYMBOLS',
|
||||
icon: 'heart-o',
|
||||
},
|
||||
icon: 'heart-o'
|
||||
}
|
||||
};
|
||||
|
||||
function fillEmoji(indice) {
|
||||
const emoji = Emojis[indice];
|
||||
return {
|
||||
name: emoji.aliases[0],
|
||||
aliases: emoji.aliases,
|
||||
aliases: emoji.aliases
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +84,7 @@ const getEmojisBySection = createSelector(
|
||||
const section = {
|
||||
...categoryToI18n[category],
|
||||
key: category,
|
||||
data: items,
|
||||
data: items
|
||||
};
|
||||
|
||||
return section;
|
||||
@@ -99,14 +94,14 @@ const getEmojisBySection = createSelector(
|
||||
|
||||
for (const [key] of customEmojis) {
|
||||
customEmojiItems.push({
|
||||
name: key,
|
||||
name: key
|
||||
});
|
||||
}
|
||||
|
||||
emoticons.push({
|
||||
...categoryToI18n.custom,
|
||||
key: 'custom',
|
||||
data: customEmojiItems,
|
||||
data: customEmojiItems
|
||||
});
|
||||
|
||||
if (recentEmojis.length) {
|
||||
@@ -115,7 +110,7 @@ const getEmojisBySection = createSelector(
|
||||
emoticons.unshift({
|
||||
...categoryToI18n.recent,
|
||||
key: 'recent',
|
||||
data: items,
|
||||
data: items
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,7 +140,7 @@ function mapStateToProps(state) {
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 2,
|
||||
maxPatternLength: 32,
|
||||
maxPatternLength: 32
|
||||
};
|
||||
|
||||
const list = emojis.length ? emojis : [];
|
||||
@@ -157,21 +152,8 @@ function mapStateToProps(state) {
|
||||
emojisBySection,
|
||||
deviceWidth,
|
||||
isLandscape: isLandscape(state),
|
||||
theme: getTheme(state),
|
||||
customEmojisEnabled: getConfig(state).EnableCustomEmoji === 'true',
|
||||
customEmojiPage: state.views.emoji.emojiPickerCustomPage,
|
||||
serverVersion: state.entities.general.serverVersion || Client4.getServerVersion(),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
getCustomEmojis,
|
||||
incrementEmojiPickerPage,
|
||||
searchCustomEmojis,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EmojiPicker);
|
||||
export default connect(mapStateToProps)(EmojiPicker);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
@@ -25,22 +25,22 @@ const style = StyleSheet.create({
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
closeButtonContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: 5,
|
||||
marginTop: 5
|
||||
},
|
||||
closeButtonText: {
|
||||
color: '#fff',
|
||||
color: '#fff'
|
||||
},
|
||||
container: {
|
||||
paddingTop: 15,
|
||||
paddingBottom: 15,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: 75,
|
||||
minHeight: 75
|
||||
},
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
@@ -49,17 +49,17 @@ const style = StyleSheet.create({
|
||||
width: deviceWidth,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'rgba(255, 116, 92, 1)',
|
||||
zIndex: 99999,
|
||||
},
|
||||
zIndex: 99999
|
||||
}
|
||||
});
|
||||
|
||||
export default class ErrorList extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
dismissError: PropTypes.func.isRequired,
|
||||
clearErrors: PropTypes.func.isRequired,
|
||||
clearErrors: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
errors: PropTypes.array.isRequired,
|
||||
errors: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
renderErrorsList() {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
@@ -16,22 +16,22 @@ const style = StyleSheet.create({
|
||||
width: 25,
|
||||
height: 25,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
buttons: {
|
||||
marginHorizontal: 15,
|
||||
marginHorizontal: 15
|
||||
},
|
||||
container: {
|
||||
alignSelf: 'stretch',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
message: {
|
||||
flex: 1,
|
||||
color: '#fff',
|
||||
},
|
||||
color: '#fff'
|
||||
}
|
||||
});
|
||||
|
||||
function GeneralError(props) {
|
||||
@@ -66,7 +66,7 @@ function GeneralError(props) {
|
||||
|
||||
GeneralError.propTypes = {
|
||||
dismiss: PropTypes.func.isRequired,
|
||||
error: PropTypes.object.isRequired,
|
||||
error: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default GeneralError;
|
||||
|
||||
@@ -11,7 +11,7 @@ import ErrorList from './error_list';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
errors: getDisplayableErrors(state),
|
||||
errors: getDisplayableErrors(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
dismissError,
|
||||
clearErrors,
|
||||
}, dispatch),
|
||||
clearErrors
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ErrorText extends PureComponent {
|
||||
static propTypes = {
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
textStyle: CustomPropTypes.Style,
|
||||
theme: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -50,15 +50,15 @@ class ErrorText extends PureComponent {
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
errorLabel: {
|
||||
color: (theme.errorTextColor || '#DA4A4A'),
|
||||
},
|
||||
color: (theme.errorTextColor || '#DA4A4A')
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
theme: getTheme(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
@@ -23,13 +23,12 @@ export default class FileAttachment extends PureComponent {
|
||||
file: PropTypes.object.isRequired,
|
||||
onInfoPress: PropTypes.func,
|
||||
onPreviewPress: PropTypes.func,
|
||||
theme: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onInfoPress: () => true,
|
||||
onPreviewPress: () => true,
|
||||
onPreviewPress: () => true
|
||||
};
|
||||
|
||||
handlePreviewPress = () => {
|
||||
@@ -62,7 +61,7 @@ export default class FileAttachment extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {file, onInfoPress, theme, navigator} = this.props;
|
||||
const {file, onInfoPress, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
let mime = file.mime_type;
|
||||
@@ -87,7 +86,6 @@ export default class FileAttachment extends PureComponent {
|
||||
<FileAttachmentDocument
|
||||
file={file}
|
||||
theme={theme}
|
||||
navigator={navigator}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -119,43 +117,43 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
downloadIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.7),
|
||||
marginRight: 5,
|
||||
marginRight: 5
|
||||
},
|
||||
fileDownloadContainer: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 3,
|
||||
marginTop: 3
|
||||
},
|
||||
fileInfo: {
|
||||
marginLeft: 2,
|
||||
fontSize: 14,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
fileInfoContainer: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 5,
|
||||
borderLeftWidth: 1,
|
||||
borderLeftColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderLeftColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
},
|
||||
fileName: {
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
marginLeft: 2,
|
||||
fontSize: 14,
|
||||
color: theme.centerChannelColor,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
fileWrapper: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginTop: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
borderColor: changeOpacity(theme.centerChannelColor, 0.2)
|
||||
},
|
||||
circularProgress: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
circularProgressContent: {
|
||||
position: 'absolute',
|
||||
@@ -164,7 +162,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
top: 0,
|
||||
left: 0,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
justifyContent: 'center'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user