Skip to content

Commit c2fb6fd

Browse files
authored
Add typing indicator using react-native-spinkit (#12)
1 parent b6eae05 commit c2fb6fd

File tree

13 files changed

+156
-15
lines changed

13 files changed

+156
-15
lines changed

Diff for: android/app/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ android {
7272
}
7373

7474
dependencies {
75+
compile project(':react-native-spinkit')
7576
compile project(':react-native-material-kit')
7677
compile fileTree(dir: "libs", include: ["*.jar"])
7778
compile "com.android.support:appcompat-v7:23.0.1"

Diff for: android/app/src/main/java/com/pubnubchat/MainActivity.java

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.pubnubchat;
22

33
import com.facebook.react.ReactActivity;
4+
import com.react.rnspinkit.RNSpinkitPackage;
45

56
public class MainActivity extends ReactActivity {
67
@Override

Diff for: android/app/src/main/java/com/pubnubchat/MainApplication.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.util.Log;
55

66
import com.facebook.react.ReactApplication;
7+
import com.react.rnspinkit.RNSpinkitPackage;
78
import com.github.xinthink.rnmk.ReactMaterialKitPackage;
89
import com.facebook.react.ReactInstanceManager;
910
import com.facebook.react.ReactNativeHost;
@@ -23,7 +24,8 @@ protected boolean getUseDeveloperSupport() {
2324
@Override
2425
protected List<ReactPackage> getPackages() {
2526
return Arrays.<ReactPackage>asList(
26-
new MainReactPackage(),
27+
new RNSpinkitPackage(),
28+
new MainReactPackage(),
2729
new ReactMaterialKitPackage()
2830
);
2931
}

Diff for: android/app/src/main/res/values/strings.xml

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<resources>
22

3+
34
<string name="app_name">Chat</string>
45
</resources>

Diff for: android/build.gradle

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ buildscript {
1313
}
1414

1515
allprojects {
16-
repositories {
16+
repositories {
17+
// Add jitpack repository (added by react-native-spinkit)
18+
maven { url "https://jitpack.io" }
1719
mavenLocal()
1820
jcenter()
1921
maven {

Diff for: android/settings.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
rootProject.name = 'Chat'
22

33
include ':app'
4+
include ':react-native-spinkit'
5+
project(':react-native-spinkit').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-spinkit/android')
46
include ':react-native-material-kit'
57
project(':react-native-material-kit').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-material-kit/android')

Diff for: components/ChatInput.js

+22-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class ChatInput extends Component {
3030
styles.bgBase,
3131
styles.pv1,
3232
styles.ph2,
33-
{maxHeight: 135},
33+
{maxHeight: 115},
3434
];
3535

3636
const inputStyle = [
@@ -49,28 +49,45 @@ export class ChatInput extends Component {
4949
<Icon name="message" size={30} color="white" />
5050
</View>
5151
<View style={[styles.h3, styles.mh2, styles.borderBHl, { borderBottomColor: 'white' }]}>
52-
<TextInput ref="txtMessage"
52+
<TextInput
5353
placeholder="Type your message"
5454
placeholderTextColor="#ccc"
55-
style={inputStyle}/>
55+
style={inputStyle}
56+
onChange={text => this.onChange(text)}
57+
/>
5658
</View>
5759
<Fab>
5860
<Icon name="send" size={25} color="white" />
5961
</Fab>
6062
</View>
61-
<View style={[styles.mt2, styles.flxRow, styles.rounded6, styles.bgSilver, styles.h2, { width: 160 }]}>
63+
<View style={[styles.mt2, styles.flxRow, styles.rounded6, styles.bgSilver, styles.h2, {width: 120}]}>
6264
<View style={[styles.rounded6, styles.w2, styles.h2, { overflow: 'hidden' }]}>
6365
<User id={currentUserId} />
6466
</View>
6567
<View style={[styles.ml1]}>
66-
<Text style={[styles.black, styles.italics, styles.f6, { marginTop: 10, fontStyle: 'italic' }]}>
68+
<Text style={[styles.black, styles.italics, styles.f6, {marginTop: 10, fontStyle: 'italic'}]}>
6769
{currentUserId}
6870
</Text>
6971
</View>
7072
</View>
7173
</View>
7274
);
7375
}
76+
77+
onChange(text) {
78+
if (this.timeout != null) {
79+
clearTimeout(this.timeout);
80+
}
81+
82+
const {setTypingState} = this.props;
83+
84+
this.timeout = setTimeout(() => {
85+
debugger;
86+
setTypingState(false);
87+
}, 1500);
88+
89+
setTypingState(true);
90+
}
7491
}
7592

7693
ChatInput.propTypes = {

Diff for: components/ChatUsersTyping.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React, {Component} from 'react';
2+
3+
import {
4+
View,
5+
Text,
6+
} from 'react-native';
7+
8+
const Spinner = require('react-native-spinkit');
9+
10+
import {User} from './User';
11+
12+
import styles from '../styles';
13+
14+
export class ChatUsersTyping extends Component {
15+
render() {
16+
const {users} = this.props;
17+
18+
const itemStyle = [
19+
styles.flxRow,
20+
styles.rounded6,
21+
styles.bgSilver,
22+
styles.h2,
23+
styles.itemsCenter,
24+
styles.jcCenter,
25+
];
26+
27+
return (
28+
<View style={[{flex: 0}, styles.flxRow, styles.m1]}>
29+
{users.map(userId =>
30+
<View key={userId} style={itemStyle}>
31+
<View style={[styles.rounded6, {overflow: 'hidden'}]}>
32+
<User id={userId} />
33+
</View>
34+
<View style={[styles.ml1]}>
35+
<Text style={[styles.black, styles.italics, styles.f6, {fontStyle: 'italic'}]}>
36+
{userId}
37+
</Text>
38+
</View>
39+
<View style={{marginTop: -5, marginLeft: 8, marginRight: 10}}>
40+
<Spinner type="ThreeBounce" />
41+
</View>
42+
</View>)}
43+
</View>
44+
);
45+
}
46+
}
47+
48+
ChatUsersTyping.propTypes = {
49+
users: React.PropTypes.array,
50+
};

Diff for: components/conversation.js

+22-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import {
1010
import {ChatHistory} from './ChatHistory';
1111
import {ChatInput} from './ChatInput';
1212
import {ChatUsers} from './ChatUsers';
13+
import {ChatUsersTyping} from './ChatUsersTyping';
1314

1415
import {conversationActions} from '../actions';
1516

1617
import {
17-
subscribe,
1818
history,
19+
publishTypingState,
20+
subscribe,
1921
} from '../services/pubnub';
2022

2123
import styles from '../styles';
@@ -28,6 +30,7 @@ class BareConversation extends Component {
2830
currentUserId,
2931
history,
3032
users,
33+
typingUsers,
3134
} = this.props;
3235

3336
const containerStyle = [
@@ -40,7 +43,11 @@ class BareConversation extends Component {
4043
<View style={containerStyle}>
4144
<ChatUsers users={users} />
4245
<ChatHistory history={history} fetchHistory={this.fetchHistory.bind(this)} />
43-
<ChatInput currentUserId={currentUserId} />
46+
<ChatUsersTyping users={typingUsers} />
47+
<ChatInput
48+
currentUserId={currentUserId}
49+
setTypingState={typing => this.onTypingStateChanged(typing)}
50+
/>
4451
</View>
4552
);
4653
}
@@ -57,10 +64,21 @@ class BareConversation extends Component {
5764
componentWillUnmount() {
5865
if (this.subscription) {
5966
this.subscription.unsubscribe();
60-
delete this.subscription;
67+
this.subscription = null;
6168
}
6269
}
6370

71+
onTypingStateChanged(typing) {
72+
if (typing) {
73+
this.props.startTyping(this.props.currentUserId);
74+
}
75+
else {
76+
this.props.stopTyping(this.props.currentUserId);
77+
}
78+
79+
publishTypingState(channel, this.props.currentUserId, typing);
80+
}
81+
6482
onMessageReceived(message) {
6583
}
6684

@@ -96,6 +114,7 @@ class BareConversation extends Component {
96114
}
97115

98116
BareConversation.propTypes = {
117+
currentUserId: PropTypes.string,
99118
users: PropTypes.array,
100119
typingUsers: PropTypes.array,
101120
history: PropTypes.array,

Diff for: ios/Chat.xcodeproj/project.pbxproj

+38-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2323
140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
2424
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
25+
17590D7464DA4569B6A1D39A /* libRNSpinkit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B93196200C3449FDBDD1B8F5 /* libRNSpinkit.a */; };
2526
6FE4C359191C47DC9D6BDE2A /* libRCTMaterialKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CAD285952B745FDBA2941AE /* libRCTMaterialKit.a */; };
2627
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
2728
B89745DF1D88537C004A571A /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B89745DE1D88537C004A571A /* MaterialIcons.ttf */; };
@@ -98,6 +99,13 @@
9899
remoteGlobalIDString = 134814201AA4EA6300B7C361;
99100
remoteInfo = RCTLinking;
100101
};
102+
831637231D8B5C2B0008AAE4 /* PBXContainerItemProxy */ = {
103+
isa = PBXContainerItemProxy;
104+
containerPortal = C1931BC8EE9A416EB4B6B460 /* RNSpinkit.xcodeproj */;
105+
proxyType = 2;
106+
remoteGlobalIDString = D42CB3511B2538DE00FD0AE2;
107+
remoteInfo = RNSpinkit;
108+
};
101109
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
102110
isa = PBXContainerItemProxy;
103111
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
@@ -138,6 +146,8 @@
138146
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
139147
8CAD285952B745FDBA2941AE /* libRCTMaterialKit.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTMaterialKit.a; sourceTree = "<group>"; };
140148
B89745DE1D88537C004A571A /* MaterialIcons.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = MaterialIcons.ttf; sourceTree = "<group>"; };
149+
B93196200C3449FDBDD1B8F5 /* libRNSpinkit.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSpinkit.a; sourceTree = "<group>"; };
150+
C1931BC8EE9A416EB4B6B460 /* RNSpinkit.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSpinkit.xcodeproj; path = "../node_modules/react-native-spinkit/ios/RNSpinkit.xcodeproj"; sourceTree = "<group>"; };
141151
DCD9F33ABEF947529CD58461 /* RCTMaterialKit.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTMaterialKit.xcodeproj; path = "../node_modules/react-native-material-kit/iOS/RCTMaterialKit.xcodeproj"; sourceTree = "<group>"; };
142152
/* End PBXFileReference section */
143153

@@ -165,6 +175,7 @@
165175
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
166176
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
167177
6FE4C359191C47DC9D6BDE2A /* libRCTMaterialKit.a in Frameworks */,
178+
17590D7464DA4569B6A1D39A /* libRNSpinkit.a in Frameworks */,
168179
);
169180
runOnlyForDeploymentPostprocessing = 0;
170181
};
@@ -274,6 +285,14 @@
274285
name = Products;
275286
sourceTree = "<group>";
276287
};
288+
831637151D8B5C2A0008AAE4 /* Products */ = {
289+
isa = PBXGroup;
290+
children = (
291+
831637241D8B5C2B0008AAE4 /* libRNSpinkit.a */,
292+
);
293+
name = Products;
294+
sourceTree = "<group>";
295+
};
277296
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
278297
isa = PBXGroup;
279298
children = (
@@ -288,6 +307,7 @@
288307
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
289308
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
290309
DCD9F33ABEF947529CD58461 /* RCTMaterialKit.xcodeproj */,
310+
C1931BC8EE9A416EB4B6B460 /* RNSpinkit.xcodeproj */,
291311
);
292312
name = Libraries;
293313
sourceTree = "<group>";
@@ -375,7 +395,7 @@
375395
83CBB9F71A601CBA00E9B192 /* Project object */ = {
376396
isa = PBXProject;
377397
attributes = {
378-
LastUpgradeCheck = 0730;
398+
LastUpgradeCheck = 730;
379399
ORGANIZATIONNAME = Facebook;
380400
TargetAttributes = {
381401
00E356ED1AD99517003FC87E = {
@@ -440,6 +460,10 @@
440460
ProductGroup = 146834001AC3E56700842450 /* Products */;
441461
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
442462
},
463+
{
464+
ProductGroup = 831637151D8B5C2A0008AAE4 /* Products */;
465+
ProjectRef = C1931BC8EE9A416EB4B6B460 /* RNSpinkit.xcodeproj */;
466+
},
443467
);
444468
projectRoot = "";
445469
targets = (
@@ -513,6 +537,13 @@
513537
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
514538
sourceTree = BUILT_PRODUCTS_DIR;
515539
};
540+
831637241D8B5C2B0008AAE4 /* libRNSpinkit.a */ = {
541+
isa = PBXReferenceProxy;
542+
fileType = archive.ar;
543+
path = libRNSpinkit.a;
544+
remoteRef = 831637231D8B5C2B0008AAE4 /* PBXContainerItemProxy */;
545+
sourceTree = BUILT_PRODUCTS_DIR;
546+
};
516547
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
517548
isa = PBXReferenceProxy;
518549
fileType = archive.ar;
@@ -621,6 +652,7 @@
621652
LIBRARY_SEARCH_PATHS = (
622653
"$(inherited)",
623654
"\"$(SRCROOT)/$(TARGET_NAME)\"",
655+
"\"$(SRCROOT)/$(TARGET_NAME)\"",
624656
);
625657
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
626658
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -639,6 +671,7 @@
639671
LIBRARY_SEARCH_PATHS = (
640672
"$(inherited)",
641673
"\"$(SRCROOT)/$(TARGET_NAME)\"",
674+
"\"$(SRCROOT)/$(TARGET_NAME)\"",
642675
);
643676
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
644677
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -654,6 +687,7 @@
654687
HEADER_SEARCH_PATHS = (
655688
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
656689
"$(SRCROOT)/../node_modules/react-native/React/**",
690+
"$(SRCROOT)/../node_modules/react-native-spinkit/ios/**",
657691
);
658692
INFOPLIST_FILE = Chat/Info.plist;
659693
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -674,6 +708,7 @@
674708
HEADER_SEARCH_PATHS = (
675709
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
676710
"$(SRCROOT)/../node_modules/react-native/React/**",
711+
"$(SRCROOT)/../node_modules/react-native-spinkit/ios/**",
677712
);
678713
INFOPLIST_FILE = Chat/Info.plist;
679714
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -727,6 +762,7 @@
727762
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
728763
"$(SRCROOT)/../node_modules/react-native/React/**",
729764
"$(SRCROOT)/../node_modules/react-native-material-kit/iOS/RCTMaterialKit",
765+
"$(SRCROOT)/../node_modules/react-native-spinkit/ios/**",
730766
);
731767
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
732768
MTL_ENABLE_DEBUG_INFO = YES;
@@ -768,6 +804,7 @@
768804
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
769805
"$(SRCROOT)/../node_modules/react-native/React/**",
770806
"$(SRCROOT)/../node_modules/react-native-material-kit/iOS/RCTMaterialKit",
807+
"$(SRCROOT)/../node_modules/react-native-spinkit/ios/**",
771808
);
772809
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
773810
MTL_ENABLE_DEBUG_INFO = NO;

0 commit comments

Comments
 (0)