diff --git a/NOTICE.txt b/NOTICE.txt index fd7ff720b..f274a29e6 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -2623,3 +2623,38 @@ 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. + +--- diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 955869eff..b0aebe205 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 7FC200E81EBB65370099331B /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */; }; 7FC649EE1FE983660074E4C7 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 349FBA7338E74D9BBD709528 /* EvilIcons.ttf */; }; 7FC649F01FE9B5D90074E4C7 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4B1B363C2414DA19C1AC521 /* OpenSans-Bold.ttf */; }; + 7FD8DEDE2029EDB7001AAC5E /* libRNTableView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FD8DEDA2029ECDF001AAC5E /* libRNTableView.a */; }; 7FDB92B11F706F58006CDFD1 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */; }; 7FEB10981F6101710039A015 /* BlurAppScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB10971F6101710039A015 /* BlurAppScreen.m */; }; 7FEB109D1F61019C0039A015 /* MattermostManaged.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109A1F61019C0039A015 /* MattermostManaged.m */; }; @@ -531,6 +532,13 @@ remoteGlobalIDString = D8AFADBD1BEE6F3F00A4592D; remoteInfo = ReactNativeNavigation; }; + 7FD8DED92029ECDF001AAC5E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7FD8DE972029ECDE001AAC5E /* RNTableView.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 873769951B7CD6E900F7C3C2; + remoteInfo = RNTableView; + }; 7FDB92A61F706F45006CDFD1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */; @@ -678,6 +686,7 @@ 7F6C47A41FE87E8C00F5A912 /* PerformRequests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PerformRequests.m; sourceTree = ""; }; 7F7D7F52201645D300D31155 /* ReactNativePermissions.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativePermissions.xcodeproj; path = "../node_modules/react-native-permissions/ios/ReactNativePermissions.xcodeproj"; sourceTree = ""; }; 7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = ""; }; + 7FD8DE972029ECDE001AAC5E /* RNTableView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNTableView.xcodeproj; path = "../node_modules/react-native-tableview/RNTableView.xcodeproj"; sourceTree = ""; }; 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = ""; }; 7FEB10961F6101710039A015 /* BlurAppScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlurAppScreen.h; path = Mattermost/BlurAppScreen.h; sourceTree = ""; }; 7FEB10971F6101710039A015 /* BlurAppScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlurAppScreen.m; path = Mattermost/BlurAppScreen.m; sourceTree = ""; }; @@ -806,6 +815,7 @@ 7FFE32EA1FD9CD050038C7A0 /* libReact.a in Frameworks */, 7FFE32E91FD9CCF40038C7A0 /* libRCTAnimation.a in Frameworks */, 7FFE32E81FD9CCDE0038C7A0 /* libRCTCameraRoll.a in Frameworks */, + 7FD8DEDE2029EDB7001AAC5E /* libRNTableView.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1201,6 +1211,14 @@ name = Products; sourceTree = ""; }; + 7FD8DE982029ECDE001AAC5E /* Products */ = { + isa = PBXGroup; + children = ( + 7FD8DEDA2029ECDF001AAC5E /* libRNTableView.a */, + ); + name = Products; + sourceTree = ""; + }; 7FDB92761F706F45006CDFD1 /* Products */ = { isa = PBXGroup; children = ( @@ -1259,6 +1277,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 7FD8DE972029ECDE001AAC5E /* RNTableView.xcodeproj */, 7F7D7F52201645D300D31155 /* ReactNativePermissions.xcodeproj */, 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */, 37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */, @@ -1592,6 +1611,10 @@ ProductGroup = 7FDF28C41E1F4B1F00DBBE56 /* Products */; ProjectRef = 2CBE9C0FB56E4FDA96C30792 /* RNSVG.xcodeproj */; }, + { + ProductGroup = 7FD8DE982029ECDE001AAC5E /* Products */; + ProjectRef = 7FD8DE972029ECDE001AAC5E /* RNTableView.xcodeproj */; + }, { ProductGroup = 7FDF28EE1E1F4B4E00DBBE56 /* Products */; ProjectRef = 2B25899FDAC149EB96ED3305 /* RNVectorIcons.xcodeproj */; @@ -2024,6 +2047,13 @@ remoteRef = 7FC200DE1EBB65100099331B /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 7FD8DEDA2029ECDF001AAC5E /* libRNTableView.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNTableView.a; + remoteRef = 7FD8DED92029ECDF001AAC5E /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -2530,6 +2560,7 @@ "$(SRCROOT)/../node_modules/react-native-video/ios", "$(SRCROOT)/../node_modules/react-native-doc-viewer/ios/**", "$(SRCROOT)/../node_modules/react-native-safe-area/ios/RNSafeArea", + "$(SRCROOT)/../node_modules/react-native-tableview/**", ); INFOPLIST_FILE = MattermostShare/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.3; @@ -2589,6 +2620,7 @@ "$(SRCROOT)/../node_modules/react-native-video/ios", "$(SRCROOT)/../node_modules/react-native-doc-viewer/ios/**", "$(SRCROOT)/../node_modules/react-native-safe-area/ios/RNSafeArea", + "$(SRCROOT)/../node_modules/react-native-tableview/**", ); INFOPLIST_FILE = MattermostShare/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.3; diff --git a/package.json b/package.json index 73bffb7cc..03b53f0b8 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-native-slider": "0.11.0", "react-native-status-bar-size": "0.3.3", "react-native-svg": "6.1.3", + "react-native-tableview": "2.1.0", "react-native-tooltip": "enahum/react-native-tooltip", "react-native-vector-icons": "4.5.0", "react-native-video": "2.0.0", diff --git a/share_extension/common/extension_channel_item.js b/share_extension/android/extension_channels/extension_channel_item.js similarity index 100% rename from share_extension/common/extension_channel_item.js rename to share_extension/android/extension_channels/extension_channel_item.js diff --git a/share_extension/android/extension_channels/extension_channels.js b/share_extension/android/extension_channels/extension_channels.js index 0b42319f0..044f28d98 100644 --- a/share_extension/android/extension_channels/extension_channels.js +++ b/share_extension/android/extension_channels/extension_channels.js @@ -17,7 +17,7 @@ import {Preferences} from 'mattermost-redux/constants'; import SearchBar from 'app/components/search_bar'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import ExtensionChannelItem from 'share_extension/common/extension_channel_item'; +import ExtensionChannelItem from './extension_channel_item'; const defaultTheme = Preferences.THEMES.default; diff --git a/share_extension/ios/extension_channels.js b/share_extension/ios/extension_channels.js index a8596bb09..cecaa9308 100644 --- a/share_extension/ios/extension_channels.js +++ b/share_extension/ios/extension_channels.js @@ -5,12 +5,13 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import { ActivityIndicator, - SectionList, Text, View } from 'react-native'; import DeviceInfo from 'react-native-device-info'; import {intlShape} from 'react-intl'; +import TableView from 'react-native-tableview'; +import Icon from 'react-native-vector-icons/FontAwesome'; import {General} from 'mattermost-redux/constants'; import {getChannelsInTeam, getDirectChannels} from 'mattermost-redux/selectors/entities/channels'; @@ -18,8 +19,6 @@ import {getChannelsInTeam, getDirectChannels} from 'mattermost-redux/selectors/e import SearchBar from 'app/components/search_bar'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import ExtensionChannelItem from 'share_extension/common/extension_channel_item'; - import ExtensionNavBar from './extension_nav_bar'; export default class ExtensionChannels extends PureComponent { @@ -112,6 +111,18 @@ export default class ExtensionChannels extends PureComponent { const direct = getDirectChannels({entities}); const channels = channelIds.map((id) => this.props.entities.channels.channels[id]).concat(direct); + const icons = await Promise.all([ + Icon.getImageSource('globe', 16, this.props.theme.centerChannelColor), + Icon.getImageSource('lock', 16, this.props.theme.centerChannelColor), + Icon.getImageSource('user', 16, this.props.theme.centerChannelColor), + Icon.getImageSource('users', 16, this.props.theme.centerChannelColor) + ]); + + this.publicChannelIcon = icons[0]; + this.privateChannelIcon = icons[1]; + this.dmChannelIcon = icons[2]; + this.gmChannelIcon = icons[3]; + this.setState({ channels }, () => { @@ -134,12 +145,20 @@ export default class ExtensionChannels extends PureComponent { }); }; - handleSelectChannel = (channel) => { - this.props.onSelectChannel(channel); - this.goBack(); + handleSelectChannel = (selected) => { + const {sections} = this.state; + const section = sections.find((s) => s.id === selected.detail); + if (section) { + const channel = section.data.find((c) => c.id === selected.value); + if (channel) { + this.props.onSelectChannel(channel); + this.goBack(); + } + } }; renderBody = (styles) => { + const {theme} = this.props; const {error, sections} = this.state; if (error) { @@ -161,45 +180,22 @@ export default class ExtensionChannels extends PureComponent { } return ( - - ); - }; - - renderItem = ({item}) => { - const {currentChannelId, theme} = this.props; - - return ( - - ); - }; - - renderItemSeparator = () => { - const {theme} = this.props; - const styles = getStyleSheet(theme); - - return ( - - - + + + {this.renderSearchBar(styles)} + + {this.renderSections()} + ); }; @@ -227,29 +223,53 @@ export default class ExtensionChannels extends PureComponent { ); }; - renderSectionHeader = ({section}) => { - const {intl} = this.context; - const {theme} = this.props; - const styles = getStyleSheet(theme); - const { - defaultMessage, - id - } = section; + renderSections = () => { + const {formatMessage} = this.context.intl; + const {currentChannelId} = this.props; + const {sections} = this.state; - return ( - - - - {intl.formatMessage({id, defaultMessage}).toUpperCase()} - - - - ); - }; + return sections.map((s) => { + const items = s.data.map((c) => { + let icon; + switch (c.type) { + case General.OPEN_CHANNEL: + icon = this.publicChannelIcon.uri; + break; + case General.PRIVATE_CHANNEL: + icon = this.privateChannelIcon.uri; + break; + case General.DM_CHANNEL: + icon = this.dmChannelIcon.uri; + break; + case General.GM_CHANNEL: + icon = this.gmChannelIcon.uri; + break; + } - sort = (a, b) => { - const locale = DeviceInfo.getDeviceLocale().split('-')[0]; - return a.localeCompare(b, locale, {numeric: true}); + return ( + + {c.display_name} + + ); + }); + + return ( + + {items} + + ); + }); }; sortDisplayName = (a, b) => { diff --git a/share_extension/ios/extension_post.js b/share_extension/ios/extension_post.js index 0dfca21fe..f4664a7f0 100644 --- a/share_extension/ios/extension_post.js +++ b/share_extension/ios/extension_post.js @@ -21,6 +21,8 @@ import LocalAuth from 'react-native-local-auth'; import RNFetchBlob from 'react-native-fetch-blob'; import {Client4} from 'mattermost-redux/client'; +import {General} from 'mattermost-redux/constants'; +import {getChannel} from 'mattermost-redux/selectors/entities/channels'; import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_utils'; import mattermostBucket from 'app/mattermost_bucket'; @@ -40,7 +42,6 @@ import { import ExtensionChannels from './extension_channels'; import ExtensionNavBar from './extension_nav_bar'; import ExtensionTeams from './extension_teams'; -import {General} from 'mattermost-redux/constants/index'; const ShareExtension = NativeModules.MattermostShare; const MAX_INPUT_HEIGHT = 95; @@ -189,13 +190,17 @@ export default class ExtensionPost extends PureComponent { const {credentials} = entities.general; const {currentUserId} = entities.users; const team = entities.teams.teams[entities.teams.currentTeamId]; - const channel = entities.channels.channels[entities.channels.currentChannelId]; + let channel = entities.channels.channels[entities.channels.currentChannelId]; const items = await ShareExtension.data(Config.AppGroupId); const text = []; const urls = []; const files = []; let totalSize = 0; + if (channel.type === General.GM_CHANNEL || channel.type === General.DM_CHANNEL) { + channel = getChannel({entities}, channel.id); + } + for (let i = 0; i < items.length; i++) { const item = items[i]; switch (item.type) { diff --git a/yarn.lock b/yarn.lock index 2a3709bb0..7e26a36f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5311,6 +5311,12 @@ react-native-tab-view@^0.0.74: dependencies: prop-types "^15.6.0" +react-native-tableview@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-native-tableview/-/react-native-tableview-2.1.0.tgz#d98b8303728b5ff4860612f05163028735bb3b07" + dependencies: + prop-types "^15.6.0" + react-native-tooltip@enahum/react-native-tooltip: version "5.1.1" resolved "https://codeload.github.com/enahum/react-native-tooltip/tar.gz/eb47f93a728813b58c01f23ec84f0b8b1bd70bd0"