Pārlūkot izejas kodu

feat(gif, rn) Added GIPHY integration on native (#11236)

Update Android build to support gif
Use GIF format instead of animated webp
Show GIFs in chat messages
Display GIF over tile
Add Giphy button in reactions menu
Added Giphy dialog
Fix isGifMessage to also allow upper case
factor2
Robert Pintilii 3 gadus atpakaļ
vecāks
revīzija
1355876f83
Revīzijas autora e-pasta adrese nav piesaistīta nevienam kontam
32 mainītis faili ar 362 papildinājumiem un 25 dzēšanām
  1. 3
    1
      android/sdk/build.gradle
  2. 1
    0
      android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java
  3. 2
    0
      android/settings.gradle
  4. 22
    0
      ios/Podfile.lock
  5. 6
    0
      ios/app/app.xcodeproj/project.pbxproj
  6. 1
    0
      lang/main.json
  7. 41
    0
      package-lock.json
  8. 1
    0
      package.json
  9. 0
    1
      react/features/app/middlewares.any.js
  10. 1
    0
      react/features/app/middlewares.native.js
  11. 1
    0
      react/features/app/middlewares.web.js
  12. 2
    1
      react/features/base/react/components/web/Message.js
  13. 11
    3
      react/features/chat/components/native/ChatMessage.js
  14. 27
    0
      react/features/chat/components/native/GifMessage.js
  15. 11
    0
      react/features/chat/components/native/styles.js
  16. 26
    10
      react/features/filmstrip/components/native/Thumbnail.js
  17. 5
    0
      react/features/filmstrip/components/native/styles.js
  18. 1
    0
      react/features/gifs/components/_.native.js
  19. 66
    0
      react/features/gifs/components/native/GifsMenu.js
  20. 3
    0
      react/features/gifs/components/native/index.js
  21. 40
    0
      react/features/gifs/components/native/styles.js
  22. 5
    4
      react/features/gifs/functions.js
  23. 0
    0
      react/features/gifs/middleware.any.js
  24. 3
    0
      react/features/gifs/middleware.native.js
  25. 2
    0
      react/features/gifs/middleware.web.js
  26. 20
    0
      react/features/gifs/subscriber.native.js
  27. 9
    0
      react/features/mobile/navigation/components/conference/components/ConferenceNavigationContainer.js
  28. 1
    0
      react/features/mobile/navigation/routes.js
  29. 5
    0
      react/features/mobile/navigation/screenOptions.js
  30. 20
    3
      react/features/reactions/components/native/ReactionButton.js
  31. 20
    2
      react/features/reactions/components/native/ReactionMenu.js
  32. 6
    0
      react/features/toolbox/components/native/styles.js

+ 3
- 1
android/sdk/build.gradle Parādīt failu

@@ -50,7 +50,8 @@ dependencies {
50 50
     api 'com.facebook.react:react-native:+'
51 51
     //noinspection GradleDynamicVersion
52 52
     implementation 'org.webkit:android-jsc:+'
53
-
53
+    
54
+    implementation 'com.facebook.fresco:animated-gif:2.5.0'
54 55
     implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
55 56
     implementation 'com.jakewharton.timber:timber:4.7.1'
56 57
     implementation 'com.squareup.duktape:duktape-android:1.3.0'
@@ -80,6 +81,7 @@ dependencies {
80 81
     implementation project(':react-native-default-preference')
81 82
     implementation project(':react-native-gesture-handler')
82 83
     implementation project(':react-native-get-random-values')
84
+    implementation project(':react-native-giphy')
83 85
     implementation project(':react-native-immersive')
84 86
     implementation project(':react-native-keep-awake')
85 87
     implementation project(':react-native-masked-view_masked-view')

+ 1
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java Parādīt failu

@@ -110,6 +110,7 @@ class ReactInstanceManagerHolder {
110 110
             new com.corbt.keepawake.KCKeepAwakePackage(),
111 111
             new com.facebook.react.shell.MainReactPackage(),
112 112
             new com.reactnativecommunity.clipboard.ClipboardPackage(),
113
+            new com.giphyreactnativesdk.GiphyReactNativeSdkPackage(),
113 114
             new com.reactnativecommunity.netinfo.NetInfoPackage(),
114 115
             new com.reactnativepagerview.PagerViewPackage(),
115 116
             new com.oblador.performance.PerformancePackage(),

+ 2
- 0
android/settings.gradle Parādīt failu

@@ -21,6 +21,8 @@ include ':react-native-gesture-handler'
21 21
 project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
22 22
 include ':react-native-get-random-values'
23 23
 project(':react-native-get-random-values').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-get-random-values/android')
24
+include ':react-native-giphy'
25
+project(':react-native-giphy').projectDir = new File(rootProject.projectDir, '../node_modules/@giphy/react-native-sdk/android')
24 26
 include ':react-native-google-signin'
25 27
 project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-google-signin/google-signin/android')
26 28
 include ':react-native-immersive'

+ 22
- 0
ios/Podfile.lock Parādīt failu

@@ -66,6 +66,11 @@ PODS:
66 66
     - GoogleUtilities/UserDefaults (~> 6.7)
67 67
     - PromisesObjC (~> 1.2)
68 68
   - fmt (6.2.1)
69
+  - Giphy (2.1.20):
70
+    - libwebp
71
+  - giphy-react-native-sdk (1.7.0):
72
+    - Giphy (= 2.1.20)
73
+    - React-Core
69 74
   - glog (0.3.5)
70 75
   - GoogleAppMeasurement (6.8.3):
71 76
     - GoogleUtilities/AppDelegateSwizzler (~> 6.7)
@@ -102,6 +107,15 @@ PODS:
102 107
     - AppAuth/Core (~> 1.4)
103 108
     - GTMSessionFetcher/Core (~> 1.5)
104 109
   - GTMSessionFetcher/Core (1.7.0)
110
+  - libwebp (1.2.1):
111
+    - libwebp/demux (= 1.2.1)
112
+    - libwebp/mux (= 1.2.1)
113
+    - libwebp/webp (= 1.2.1)
114
+  - libwebp/demux (1.2.1):
115
+    - libwebp/webp
116
+  - libwebp/mux (1.2.1):
117
+    - libwebp/demux
118
+  - libwebp/webp (1.2.1)
105 119
   - nanopb (1.30906.0):
106 120
     - nanopb/decode (= 1.30906.0)
107 121
     - nanopb/encode (= 1.30906.0)
@@ -442,6 +456,7 @@ DEPENDENCIES:
442 456
   - Firebase/Analytics (~> 6.33.0)
443 457
   - Firebase/Crashlytics (~> 6.33.0)
444 458
   - Firebase/DynamicLinks (~> 6.33.0)
459
+  - "giphy-react-native-sdk (from `../node_modules/@giphy/react-native-sdk`)"
445 460
   - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
446 461
   - ObjectiveDropboxOfficial (= 6.2.3)
447 462
   - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
@@ -510,12 +525,14 @@ SPEC REPOS:
510 525
     - FirebaseDynamicLinks
511 526
     - FirebaseInstallations
512 527
     - fmt
528
+    - Giphy
513 529
     - GoogleAppMeasurement
514 530
     - GoogleDataTransport
515 531
     - GoogleSignIn
516 532
     - GoogleUtilities
517 533
     - GTMAppAuth
518 534
     - GTMSessionFetcher
535
+    - libwebp
519 536
     - nanopb
520 537
     - ObjectiveDropboxOfficial
521 538
     - PromisesObjC
@@ -531,6 +548,8 @@ EXTERNAL SOURCES:
531 548
     :path: "../node_modules/react-native/Libraries/FBLazyVector"
532 549
   FBReactNativeSpec:
533 550
     :path: "../node_modules/react-native/React/FBReactNativeSpec"
551
+  giphy-react-native-sdk:
552
+    :path: "../node_modules/@giphy/react-native-sdk"
534 553
   glog:
535 554
     :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
536 555
   RCT-Folly:
@@ -651,6 +670,8 @@ SPEC CHECKSUMS:
651 670
   FirebaseDynamicLinks: 6eac37d86910382eafb6315d952cc44c9e176094
652 671
   FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
653 672
   fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
673
+  Giphy: b6d5087521d251bb8c99cdc0eb07bbdf86d142d5
674
+  giphy-react-native-sdk: 7abccf2b52123a0f30ce99da895ab6288023680c
654 675
   glog: 5337263514dd6f09803962437687240c5dc39aa4
655 676
   GoogleAppMeasurement: 966e88df9d19c15715137bb2ddaf52373f111436
656 677
   GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
@@ -658,6 +679,7 @@ SPEC CHECKSUMS:
658 679
   GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
659 680
   GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
660 681
   GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
682
+  libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
661 683
   nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
662 684
   ObjectiveDropboxOfficial: fe206ce8c0bc49976c249d472db7fdbc53ebbd53
663 685
   PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97

+ 6
- 0
ios/app/app.xcodeproj/project.pbxproj Parādīt failu

@@ -38,6 +38,8 @@
38 38
 		DEA9F28A258A6EA800D4CD74 /* JitsiMeetSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
39 39
 		E588011722789D43008B0561 /* JitsiMeetContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58801132278944E008B0561 /* JitsiMeetContext.swift */; };
40 40
 		E5C97B63227A1EB400199214 /* JitsiMeetCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */; };
41
+		FD572B9827EDF32300A800FB /* GiphyUISDK.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */; };
42
+		FD572B9927EDF32300A800FB /* GiphyUISDK.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
41 43
 /* End PBXBuildFile section */
42 44
 
43 45
 /* Begin PBXContainerItemProxy section */
@@ -72,6 +74,7 @@
72 74
 			dstSubfolderSpec = 10;
73 75
 			files = (
74 76
 				DEA9F28A258A6EA800D4CD74 /* JitsiMeetSDK.framework in Embed Frameworks */,
77
+				FD572B9927EDF32300A800FB /* GiphyUISDK.xcframework in Embed Frameworks */,
75 78
 				DE05038A256E904600DEE3A5 /* WebRTC.xcframework in Embed Frameworks */,
76 79
 			);
77 80
 			name = "Embed Frameworks";
@@ -158,6 +161,7 @@
158 161
 		DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.xcframework"; sourceTree = "<group>"; };
159 162
 		E58801132278944E008B0561 /* JitsiMeetContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetContext.swift; sourceTree = "<group>"; };
160 163
 		E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetCommands.swift; sourceTree = "<group>"; };
164
+		FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GiphyUISDK.xcframework; path = ../Pods/Giphy/GiphySDK/GiphyUISDK.xcframework; sourceTree = "<group>"; };
161 165
 /* End PBXFileReference section */
162 166
 
163 167
 /* Begin PBXFrameworksBuildPhase section */
@@ -174,6 +178,7 @@
174 178
 			files = (
175 179
 				DE050389256E904600DEE3A5 /* WebRTC.xcframework in Frameworks */,
176 180
 				DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */,
181
+				FD572B9827EDF32300A800FB /* GiphyUISDK.xcframework in Frameworks */,
177 182
 				2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */,
178 183
 			);
179 184
 			runOnlyForDeploymentPostprocessing = 0;
@@ -199,6 +204,7 @@
199 204
 		0B26BE711EC5BC4D00EEFB41 /* Frameworks */ = {
200 205
 			isa = PBXGroup;
201 206
 			children = (
207
+				FD572B9727EDF32300A800FB /* GiphyUISDK.xcframework */,
202 208
 				DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */,
203 209
 				DE050388256E904600DEE3A5 /* WebRTC.xcframework */,
204 210
 				0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */,

+ 1
- 0
lang/main.json Parādīt failu

@@ -629,6 +629,7 @@
629 629
         "displayNotifications": "Display notifications for",
630 630
         "focus": "Conference focus",
631 631
         "focusFail": "{{component}} not available - retry in {{ms}} sec",
632
+        "gifsMenu": "GIPHY",
632 633
         "groupTitle": "Notifications",
633 634
         "hostAskedUnmute": "The moderator would like you to speak",
634 635
         "invitedOneMember": "{{name}} has been invited",

+ 41
- 0
package-lock.json Parādīt failu

@@ -30,6 +30,7 @@
30 30
         "@atlaskit/tooltip": "17.1.2",
31 31
         "@giphy/js-fetch-api": "4.1.2",
32 32
         "@giphy/react-components": "5.6.0",
33
+        "@giphy/react-native-sdk": "1.7.0",
33 34
         "@hapi/bourne": "2.0.0",
34 35
         "@jitsi/js-utils": "2.0.0",
35 36
         "@jitsi/logger": "2.0.0",
@@ -3378,6 +3379,30 @@
3378 3379
         "react": ">=16.3.0"
3379 3380
       }
3380 3381
     },
3382
+    "node_modules/@giphy/react-native-sdk": {
3383
+      "version": "1.7.0",
3384
+      "resolved": "https://registry.npmjs.org/@giphy/react-native-sdk/-/react-native-sdk-1.7.0.tgz",
3385
+      "integrity": "sha512-mCIqtPkDAstL+BDTbC1EQ4SiRkND3zd9uLKUeR4RkK2AhjRTUIheGzfxOZrdR014LVwcwKw5s9qpogoXr66mgw==",
3386
+      "dependencies": {
3387
+        "@giphy/js-types": "^4.0.3",
3388
+        "type-fest": "^2.10.0"
3389
+      },
3390
+      "peerDependencies": {
3391
+        "react": "*",
3392
+        "react-native": "*"
3393
+      }
3394
+    },
3395
+    "node_modules/@giphy/react-native-sdk/node_modules/type-fest": {
3396
+      "version": "2.12.1",
3397
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.1.tgz",
3398
+      "integrity": "sha512-AiknQSEqKVGDDjtZqeKrUoTlcj7FKhupmnVUgz6KoOKtvMwRGE6hUNJ/nVear+h7fnUPO1q/htSkYKb1pyntkQ==",
3399
+      "engines": {
3400
+        "node": ">=12.20"
3401
+      },
3402
+      "funding": {
3403
+        "url": "https://github.com/sponsors/sindresorhus"
3404
+      }
3405
+    },
3381 3406
     "node_modules/@hapi/bourne": {
3382 3407
       "version": "2.0.0",
3383 3408
       "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.0.0.tgz",
@@ -22246,6 +22271,22 @@
22246 22271
         }
22247 22272
       }
22248 22273
     },
22274
+    "@giphy/react-native-sdk": {
22275
+      "version": "1.7.0",
22276
+      "resolved": "https://registry.npmjs.org/@giphy/react-native-sdk/-/react-native-sdk-1.7.0.tgz",
22277
+      "integrity": "sha512-mCIqtPkDAstL+BDTbC1EQ4SiRkND3zd9uLKUeR4RkK2AhjRTUIheGzfxOZrdR014LVwcwKw5s9qpogoXr66mgw==",
22278
+      "requires": {
22279
+        "@giphy/js-types": "^4.0.3",
22280
+        "type-fest": "^2.10.0"
22281
+      },
22282
+      "dependencies": {
22283
+        "type-fest": {
22284
+          "version": "2.12.1",
22285
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.1.tgz",
22286
+          "integrity": "sha512-AiknQSEqKVGDDjtZqeKrUoTlcj7FKhupmnVUgz6KoOKtvMwRGE6hUNJ/nVear+h7fnUPO1q/htSkYKb1pyntkQ=="
22287
+        }
22288
+      }
22289
+    },
22249 22290
     "@hapi/bourne": {
22250 22291
       "version": "2.0.0",
22251 22292
       "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.0.0.tgz",

+ 1
- 0
package.json Parādīt failu

@@ -35,6 +35,7 @@
35 35
     "@atlaskit/tooltip": "17.1.2",
36 36
     "@giphy/js-fetch-api": "4.1.2",
37 37
     "@giphy/react-components": "5.6.0",
38
+    "@giphy/react-native-sdk": "1.7.0",
38 39
     "@hapi/bourne": "2.0.0",
39 40
     "@jitsi/js-utils": "2.0.0",
40 41
     "@jitsi/logger": "2.0.0",

+ 0
- 1
react/features/app/middlewares.any.js Parādīt failu

@@ -30,7 +30,6 @@ import '../display-name/middleware';
30 30
 import '../etherpad/middleware';
31 31
 import '../filmstrip/middleware';
32 32
 import '../follow-me/middleware';
33
-import '../gifs/middleware';
34 33
 import '../invite/middleware';
35 34
 import '../jaas/middleware';
36 35
 import '../large-video/middleware';

+ 1
- 0
react/features/app/middlewares.native.js Parādīt failu

@@ -1,6 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import '../authentication/middleware';
4
+import '../gifs/middleware';
4 5
 import '../mobile/audio-mode/middleware';
5 6
 import '../mobile/background/middleware';
6 7
 import '../mobile/call-integration/middleware';

+ 1
- 0
react/features/app/middlewares.web.js Parādīt failu

@@ -22,5 +22,6 @@ import '../talk-while-muted/middleware';
22 22
 import '../virtual-background/middleware';
23 23
 import '../face-centering/middleware';
24 24
 import '../facial-recognition/middleware';
25
+import '../gifs/middleware';
25 26
 
26 27
 import './middlewares.any';

+ 2
- 1
react/features/base/react/components/web/Message.js Parādīt failu

@@ -4,6 +4,7 @@ import React, { Component } from 'react';
4 4
 import { toArray } from 'react-emoji-render';
5 5
 
6 6
 import GifMessage from '../../../../chat/components/web/GifMessage';
7
+import { GIF_PREFIX } from '../../../../gifs/constants';
7 8
 import { isGifMessage } from '../../../../gifs/functions';
8 9
 
9 10
 import Linkify from './Linkify';
@@ -49,7 +50,7 @@ class Message extends Component<Props> {
49 50
 
50 51
         // check if the message is a GIF
51 52
         if (isGifMessage(text)) {
52
-            const url = text.substring(4, text.length - 1);
53
+            const url = text.substring(GIF_PREFIX.length, text.length - 1);
53 54
 
54 55
             content.push(<GifMessage
55 56
                 key = { url }

+ 11
- 3
react/features/chat/components/native/ChatMessage.js Parādīt failu

@@ -9,10 +9,12 @@ import { translate } from '../../../base/i18n';
9 9
 import { Linkify } from '../../../base/react';
10 10
 import { connect } from '../../../base/redux';
11 11
 import { type StyleType } from '../../../base/styles';
12
+import { isGifMessage } from '../../../gifs/functions';
12 13
 import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
13 14
 import { replaceNonUnicodeEmojis } from '../../functions';
14 15
 import AbstractChatMessage, { type Props as AbstractProps } from '../AbstractChatMessage';
15 16
 
17
+import GifMessage from './GifMessage';
16 18
 import PrivateMessageButton from './PrivateMessageButton';
17 19
 import styles from './styles';
18 20
 
@@ -75,6 +77,8 @@ class ChatMessage extends AbstractChatMessage<Props> {
75 77
             messageBubbleStyle.push(_styles.lobbyMessageBubble);
76 78
         }
77 79
 
80
+        const messageText = replaceNonUnicodeEmojis(this._getMessageText());
81
+
78 82
         return (
79 83
             <View style = { styles.messageWrapper } >
80 84
                 { this._renderAvatar() }
@@ -82,9 +86,13 @@ class ChatMessage extends AbstractChatMessage<Props> {
82 86
                     <View style = { messageBubbleStyle }>
83 87
                         <View style = { styles.textWrapper } >
84 88
                             { this._renderDisplayName() }
85
-                            <Linkify linkStyle = { styles.chatLink }>
86
-                                { replaceNonUnicodeEmojis(this._getMessageText()) }
87
-                            </Linkify>
89
+                            {isGifMessage(messageText)
90
+                                ? <GifMessage message = { messageText } />
91
+                                : (
92
+                                    <Linkify linkStyle = { styles.chatLink }>
93
+                                        {messageText}
94
+                                    </Linkify>
95
+                                )}
88 96
                             { this._renderPrivateNotice() }
89 97
                         </View>
90 98
                         { this._renderPrivateReplyButton() }

+ 27
- 0
react/features/chat/components/native/GifMessage.js Parādīt failu

@@ -0,0 +1,27 @@
1
+import React from 'react';
2
+import { Image, View } from 'react-native';
3
+
4
+import { GIF_PREFIX } from '../../../gifs/constants';
5
+
6
+import styles from './styles';
7
+
8
+type Props = {
9
+
10
+    /**
11
+     * The formatted gif message.
12
+     */
13
+    message: string
14
+}
15
+
16
+const GifMessage = ({ message }: Props) => {
17
+    const url = message.substring(GIF_PREFIX.length, message.length - 1);
18
+
19
+    return (<View
20
+        style = { styles.gifContainer }>
21
+        <Image
22
+            source = {{ uri: url }}
23
+            style = { styles.gifImage } />
24
+    </View>);
25
+};
26
+
27
+export default GifMessage;

+ 11
- 0
react/features/chat/components/native/styles.js Parādīt failu

@@ -145,6 +145,17 @@ export default {
145 145
         borderTopLeftRadius: 0,
146 146
         borderTopRightRadius: 0,
147 147
         borderBottomRightRadius: 0
148
+    },
149
+
150
+    gifContainer: {
151
+        maxHeight: 150
152
+    },
153
+
154
+    gifImage: {
155
+        resizeMode: 'contain',
156
+        width: 250,
157
+        height: undefined,
158
+        flexGrow: 1
148 159
     }
149 160
 };
150 161
 

+ 26
- 10
react/features/filmstrip/components/native/Thumbnail.js Parādīt failu

@@ -1,7 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import React, { PureComponent } from 'react';
4
-import { View } from 'react-native';
4
+import { Image, View } from 'react-native';
5 5
 import type { Dispatch } from 'redux';
6 6
 
7 7
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
@@ -22,6 +22,7 @@ import { StyleType } from '../../../base/styles';
22 22
 import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
23 23
 import { ConnectionIndicator } from '../../../connection-indicator';
24 24
 import { DisplayNameLabel } from '../../../display-name';
25
+import { getGifDisplayMode, getGifForParticipant } from '../../../gifs/functions';
25 26
 import {
26 27
     showContextMenuDetails,
27 28
     showSharedVideoMenu
@@ -45,6 +46,11 @@ type Props = {
45 46
      */
46 47
     _audioMuted: boolean,
47 48
 
49
+    /**
50
+     * URL of GIF sent by this participant, null if there's none.
51
+     */
52
+    _gifSrc: ?string,
53
+
48 54
     /**
49 55
      * Indicates whether the participant is fake.
50 56
      */
@@ -247,6 +253,7 @@ class Thumbnail extends PureComponent<Props> {
247 253
      */
248 254
     render() {
249 255
         const {
256
+            _gifSrc,
250 257
             _isScreenShare: isScreenShare,
251 258
             _isFakeParticipant,
252 259
             _participantId: participantId,
@@ -280,15 +287,21 @@ class Thumbnail extends PureComponent<Props> {
280 287
                     _renderDominantSpeakerIndicator ? styles.thumbnailDominantSpeaker : null
281 288
                 ] }
282 289
                 touchFeedback = { false }>
283
-                <ParticipantView
284
-                    avatarSize = { tileView ? AVATAR_SIZE * 1.5 : AVATAR_SIZE }
285
-                    disableVideo = { isScreenShare || _isFakeParticipant }
286
-                    participantId = { participantId }
287
-                    tintEnabled = { participantInLargeVideo && !disableTint }
288
-                    tintStyle = { _styles.activeThumbnailTint }
289
-                    zOrder = { 1 } />
290
-                {
291
-                    this._renderIndicators()
290
+                {_gifSrc ? <Image
291
+                    source = {{ uri: _gifSrc }}
292
+                    style = { styles.thumbnailGif } />
293
+                    : <>
294
+                        <ParticipantView
295
+                            avatarSize = { tileView ? AVATAR_SIZE * 1.5 : AVATAR_SIZE }
296
+                            disableVideo = { isScreenShare || _isFakeParticipant }
297
+                            participantId = { participantId }
298
+                            tintEnabled = { participantInLargeVideo && !disableTint }
299
+                            tintStyle = { _styles.activeThumbnailTint }
300
+                            zOrder = { 1 } />
301
+                        {
302
+                            this._renderIndicators()
303
+                        }
304
+                    </>
292 305
                 }
293 306
             </Container>
294 307
         );
@@ -324,9 +337,12 @@ function _mapStateToProps(state, ownProps) {
324 337
     const renderModeratorIndicator = !_isEveryoneModerator
325 338
         && participant?.role === PARTICIPANT_ROLE.MODERATOR;
326 339
     const participantInLargeVideo = id === largeVideo.participantId;
340
+    const { gifUrl: gifSrc } = getGifForParticipant(state, id);
341
+    const mode = getGifDisplayMode(state);
327 342
 
328 343
     return {
329 344
         _audioMuted: audioTrack?.muted ?? true,
345
+        _gifSrc: mode === 'chat' ? null : gifSrc,
330 346
         _isFakeParticipant: participant?.isFakeParticipant,
331 347
         _isScreenShare: isScreenShare,
332 348
         _local: participant?.local,

+ 5
- 0
react/features/filmstrip/components/native/styles.js Parādīt failu

@@ -171,6 +171,11 @@ export default {
171 171
     thumbnailDominantSpeaker: {
172 172
         borderWidth: 4,
173 173
         borderColor: BaseTheme.palette.action01Hover
174
+    },
175
+
176
+    thumbnailGif: {
177
+        flexGrow: 1,
178
+        resizeMode: 'contain'
174 179
     }
175 180
 };
176 181
 

+ 1
- 0
react/features/gifs/components/_.native.js Parādīt failu

@@ -0,0 +1 @@
1
+export * from './native';

+ 66
- 0
react/features/gifs/components/native/GifsMenu.js Parādīt failu

@@ -0,0 +1,66 @@
1
+import { GiphyContent, GiphyGridView, GiphyMediaType } from '@giphy/react-native-sdk';
2
+import React, { useCallback, useState } from 'react';
3
+import { Image, Keyboard, Text, View } from 'react-native';
4
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
+import { useDispatch } from 'react-redux';
6
+
7
+import { createGifSentEvent, sendAnalytics } from '../../../analytics';
8
+import JitsiScreen from '../../../base/modal/components/JitsiScreen';
9
+import { sendMessage } from '../../../chat/actions.any';
10
+import { goBack } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
11
+import ClearableInput from '../../../participants-pane/components/native/ClearableInput';
12
+import { formatGifUrlMessage, getGifUrl } from '../../functions';
13
+
14
+import styles from './styles';
15
+
16
+const GifsMenu = () => {
17
+    const [ searchQuery, setSearchQuery ] = useState('');
18
+    const dispatch = useDispatch();
19
+    const insets = useSafeAreaInsets();
20
+
21
+    const content = searchQuery === ''
22
+        ? GiphyContent.trending({ mediaType: GiphyMediaType.Gif })
23
+        : GiphyContent.search({
24
+            searchQuery,
25
+            mediaType: GiphyMediaType.Gif
26
+        });
27
+
28
+    const sendGif = useCallback(e => {
29
+        const url = getGifUrl(e.nativeEvent.media);
30
+
31
+        sendAnalytics(createGifSentEvent());
32
+
33
+        dispatch(sendMessage(formatGifUrlMessage(url), true));
34
+        goBack();
35
+    }, []);
36
+
37
+    const onScroll = useCallback(Keyboard.dismiss, []);
38
+
39
+    return (<JitsiScreen
40
+        style = { styles.container }>
41
+        <ClearableInput
42
+            autoFocus = { true }
43
+            customStyles = { styles.clearableInput }
44
+            onChange = { setSearchQuery }
45
+            placeholder = 'Search GIPHY'
46
+            value = { searchQuery } />
47
+        <GiphyGridView
48
+            cellPadding = { 5 }
49
+            content = { content }
50
+            onMediaSelect = { sendGif }
51
+            onScroll = { onScroll }
52
+            style = { styles.grid } />
53
+        <View
54
+            style = { [ styles.credit, {
55
+                bottom: insets.bottom,
56
+                left: insets.left,
57
+                right: insets.right
58
+            } ] }>
59
+            <Text
60
+                style = { styles.creditText }>Powered by</Text>
61
+            <Image source = { require('../../../../../images/GIPHY_logo.png') } />
62
+        </View>
63
+    </JitsiScreen>);
64
+};
65
+
66
+export default GifsMenu;

+ 3
- 0
react/features/gifs/components/native/index.js Parādīt failu

@@ -0,0 +1,3 @@
1
+// @flow
2
+
3
+export { default as GifsMenu } from './GifsMenu';

+ 40
- 0
react/features/gifs/components/native/styles.js Parādīt failu

@@ -0,0 +1,40 @@
1
+import BaseTheme from '../../../base/ui/components/BaseTheme.native';
2
+
3
+export default {
4
+    container: {
5
+        backgroundColor: BaseTheme.palette.ui01,
6
+        flex: 1
7
+    },
8
+
9
+    clearableInput: {
10
+        wrapper: {
11
+            marginBottom: BaseTheme.spacing[3],
12
+            marginTop: BaseTheme.spacing[3]
13
+        },
14
+
15
+        input: { textAlign: 'left' }
16
+    },
17
+
18
+    grid: {
19
+        flex: 1,
20
+        marginLeft: BaseTheme.spacing[3],
21
+        marginRight: BaseTheme.spacing[3]
22
+    },
23
+
24
+    credit: {
25
+        backgroundColor: BaseTheme.palette.ui01,
26
+        width: '100%',
27
+        height: 40,
28
+        position: 'absolute',
29
+        marginBottom: 0,
30
+        display: 'flex',
31
+        flexDirection: 'row',
32
+        alignItems: 'center',
33
+        justifyContent: 'center'
34
+    },
35
+
36
+    creditText: {
37
+        color: 'white',
38
+        fontWeight: 'bold'
39
+    }
40
+};

+ 5
- 4
react/features/gifs/functions.js Parādīt failu

@@ -10,7 +10,7 @@ import { GIF_PREFIX } from './constants';
10 10
  * @returns {Object}
11 11
  */
12 12
 export function getGifForParticipant(state, participantId) {
13
-    return state['features/gifs'].gifList.get(participantId) || {};
13
+    return isGifEnabled(state) ? state['features/gifs'].gifList.get(participantId) || {} : {};
14 14
 }
15 15
 
16 16
 /**
@@ -20,7 +20,8 @@ export function getGifForParticipant(state, participantId) {
20 20
  * @returns {boolean}
21 21
  */
22 22
 export function isGifMessage(message) {
23
-    return message.trim().startsWith(GIF_PREFIX);
23
+    return message.trim().toLowerCase()
24
+        .startsWith(GIF_PREFIX);
24 25
 }
25 26
 
26 27
 /**
@@ -43,11 +44,11 @@ export function isGifsMenuOpen(state) {
43 44
  * @returns {boolean}
44 45
  */
45 46
 export function getGifUrl(gif) {
46
-    const embedUrl = gif?.embed_url || '';
47
+    const embedUrl = gif?.embed_url || gif?.data?.embed_url || '';
47 48
     const idx = embedUrl.lastIndexOf('/');
48 49
     const id = embedUrl.substr(idx + 1);
49 50
 
50
-    return `https://i.giphy.com/media/${id}/giphy.webp`;
51
+    return `https://i.giphy.com/media/${id}/giphy.gif`;
51 52
 }
52 53
 
53 54
 /**

react/features/gifs/middleware.js → react/features/gifs/middleware.any.js Parādīt failu


+ 3
- 0
react/features/gifs/middleware.native.js Parādīt failu

@@ -0,0 +1,3 @@
1
+
2
+import './middleware.any';
3
+import './subscriber.native';

+ 2
- 0
react/features/gifs/middleware.web.js Parādīt failu

@@ -0,0 +1,2 @@
1
+
2
+import './middleware.any';

+ 20
- 0
react/features/gifs/subscriber.native.js Parādīt failu

@@ -0,0 +1,20 @@
1
+import { GiphySDK } from '@giphy/react-native-sdk';
2
+
3
+import { StateListenerRegistry } from '../base/redux';
4
+
5
+import { isGifEnabled } from './functions';
6
+
7
+/**
8
+ * Listens for changes in the number of participants to calculate the dimensions of the tile view grid and the tiles.
9
+ */
10
+StateListenerRegistry.register(
11
+    /* selector */ state => state['features/base/config']?.giphy,
12
+    /* listener */ (_, store) => {
13
+        const state = store.getState();
14
+
15
+        if (isGifEnabled(state)) {
16
+            GiphySDK.configure({ apiKey: state['features/base/config'].giphy?.sdkKey });
17
+        }
18
+    }, {
19
+        deepEquals: true
20
+    });

+ 9
- 0
react/features/mobile/navigation/components/conference/components/ConferenceNavigationContainer.js Parādīt failu

@@ -10,6 +10,7 @@ import { Chat } from '../../../../../chat';
10 10
 import Conference from '../../../../../conference/components/native/Conference';
11 11
 import { getDisablePolls } from '../../../../../conference/functions';
12 12
 import { SharedDocument } from '../../../../../etherpad';
13
+import { GifsMenu } from '../../../../../gifs/components';
13 14
 import AddPeopleDialog
14 15
     from '../../../../../invite/components/add-people-dialog/native/AddPeopleDialog';
15 16
 import LobbyScreen from '../../../../../lobby/components/native/LobbyScreen';
@@ -27,6 +28,7 @@ import { screen } from '../../../routes';
27 28
 import {
28 29
     chatScreenOptions,
29 30
     conferenceScreenOptions,
31
+    gifsMenuOptions,
30 32
     inviteScreenOptions,
31 33
     liveStreamScreenOptions,
32 34
     lobbyScreenOptions,
@@ -124,6 +126,13 @@ const ConferenceNavigationContainer = () => {
124 126
                         ...salesforceScreenOptions,
125 127
                         title: t('notify.linkToSalesforce')
126 128
                     }} />
129
+                <ConferenceStack.Screen
130
+                    component = { GifsMenu }
131
+                    name = { screen.conference.gifsMenu }
132
+                    options = {{
133
+                        ...gifsMenuOptions,
134
+                        title: t('notify.gifsMenu')
135
+                    }} />
127 136
                 <ConferenceStack.Screen
128 137
                     component = { LobbyScreen }
129 138
                     name = { screen.lobby }

+ 1
- 0
react/features/mobile/navigation/routes.js Parādīt failu

@@ -30,6 +30,7 @@ export const screen = {
30 30
         speakerStats: 'Speaker Stats',
31 31
         salesforce: 'Link to Salesforce',
32 32
         participants: 'Participants',
33
+        gifsMenu: 'GIPHY',
33 34
         invite: 'Invite',
34 35
         sharedDocument: 'Shared document'
35 36
     },

+ 5
- 0
react/features/mobile/navigation/screenOptions.js Parādīt failu

@@ -278,6 +278,11 @@ export const liveStreamScreenOptions = {
278 278
  */
279 279
 export const salesforceScreenOptions = presentationScreenOptions;
280 280
 
281
+/**
282
+ * Screen options for GIPHY integration modal.
283
+ */
284
+export const gifsMenuOptions = presentationScreenOptions;
285
+
281 286
 /**
282 287
  * Screen options for shared document.
283 288
  */

+ 20
- 3
react/features/reactions/components/native/ReactionButton.js Parādīt failu

@@ -13,6 +13,11 @@ import { REACTIONS } from '../../constants';
13 13
 
14 14
 export type ReactionStyles = {
15 15
 
16
+    /**
17
+     * Style for the gif button.
18
+     */
19
+    gifButton: StyleType,
20
+
16 21
     /**
17 22
      * Style for the button.
18 23
      */
@@ -45,6 +50,16 @@ export type ReactionStyles = {
45 50
  */
46 51
 type Props = {
47 52
 
53
+    /**
54
+     * Component children.
55
+     */
56
+    children?: ReactNode,
57
+
58
+    /**
59
+     * External click handler.
60
+     */
61
+    onClick?: Function,
62
+
48 63
     /**
49 64
      * Collection of styles for the button.
50 65
      */
@@ -67,6 +82,8 @@ type Props = {
67 82
  * @returns {ReactElement}
68 83
  */
69 84
 function ReactionButton({
85
+    children,
86
+    onClick,
70 87
     styles,
71 88
     reaction,
72 89
     t
@@ -81,10 +98,10 @@ function ReactionButton({
81 98
         <TouchableHighlight
82 99
             accessibilityLabel = { t(`toolbar.accessibilityLabel.${reaction}`) }
83 100
             accessibilityRole = 'button'
84
-            onPress = { _onClick }
85
-            style = { styles.style }
101
+            onPress = { onClick || _onClick }
102
+            style = { [ styles.style, children && styles?.gifButton ] }
86 103
             underlayColor = { styles.underlayColor }>
87
-            <Text style = { styles.emoji }>{REACTIONS[reaction].emoji}</Text>
104
+            {children ?? <Text style = { styles.emoji }>{REACTIONS[reaction].emoji}</Text>}
88 105
         </TouchableHighlight>
89 106
     );
90 107
 }

+ 20
- 2
react/features/reactions/components/native/ReactionMenu.js Parādīt failu

@@ -1,10 +1,13 @@
1 1
 // @flow
2 2
 
3
-import React from 'react';
4
-import { View } from 'react-native';
3
+import React, { useCallback } from 'react';
4
+import { Image, View } from 'react-native';
5 5
 import { useSelector } from 'react-redux';
6 6
 
7 7
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
8
+import { isGifEnabled } from '../../../gifs/functions';
9
+import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
10
+import { screen } from '../../../mobile/navigation/routes';
8 11
 import { REACTIONS } from '../../constants';
9 12
 
10 13
 import RaiseHandButton from './RaiseHandButton';
@@ -36,6 +39,12 @@ function ReactionMenu({
36 39
     overflowMenu
37 40
 }: Props) {
38 41
     const _styles = useSelector(state => ColorSchemeRegistry.get(state, 'Toolbox'));
42
+    const gifEnabled = useSelector(isGifEnabled);
43
+
44
+    const openGifMenu = useCallback(() => {
45
+        navigate(screen.conference.gifsMenu);
46
+        onCancel();
47
+    }, []);
39 48
 
40 49
     return (
41 50
         <View style = { overflowMenu ? _styles.overflowReactionMenu : _styles.reactionMenu }>
@@ -46,6 +55,15 @@ function ReactionMenu({
46 55
                         reaction = { key }
47 56
                         styles = { _styles.reactionButton } />
48 57
                 ))}
58
+                {gifEnabled && (
59
+                    <ReactionButton
60
+                        onClick = { openGifMenu }
61
+                        styles = { _styles.reactionButton }>
62
+                        <Image
63
+                            height = { 22 }
64
+                            source = { require('../../../../../images/GIPHY_icon.png') } />
65
+                    </ReactionButton>
66
+                )}
49 67
             </View>
50 68
             <RaiseHandButton onCancel = { onCancel } />
51 69
         </View>

+ 6
- 0
react/features/toolbox/components/native/styles.js Parādīt failu

@@ -51,6 +51,11 @@ const reactionButton = {
51 51
     marginHorizontal: 0
52 52
 };
53 53
 
54
+const gifButton = {
55
+    ...reactionButton,
56
+    backgroundColor: '#000'
57
+};
58
+
54 59
 /**
55 60
  * The style of the emoji on the reaction buttons.
56 61
  */
@@ -161,6 +166,7 @@ ColorSchemeRegistry.register('Toolbox', {
161 166
     },
162 167
 
163 168
     reactionButton: {
169
+        gifButton,
164 170
         style: reactionButton,
165 171
         underlayColor: BaseTheme.palette.ui13,
166 172
         emoji: reactionEmoji

Notiek ielāde…
Atcelt
Saglabāt