diff --git a/android/app/src/main/res/drawable-hdpi/eye.png b/android/app/src/main/res/drawable-hdpi/eye.png
new file mode 100644
index 00000000..1305c09e
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/eye.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/eye_slash.png b/android/app/src/main/res/drawable-hdpi/eye_slash.png
new file mode 100644
index 00000000..61209fdd
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/eye_slash.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/hashtag.png b/android/app/src/main/res/drawable-hdpi/hashtag.png
index 946eae13..5d4be58c 100644
Binary files a/android/app/src/main/res/drawable-hdpi/hashtag.png and b/android/app/src/main/res/drawable-hdpi/hashtag.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_facebook.png b/android/app/src/main/res/drawable-hdpi/icon_facebook.png
new file mode 100644
index 00000000..5c637c07
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_facebook.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_github.png b/android/app/src/main/res/drawable-hdpi/icon_github.png
new file mode 100644
index 00000000..32dbe331
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_github.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_gitlab.png b/android/app/src/main/res/drawable-hdpi/icon_gitlab.png
new file mode 100644
index 00000000..ce6c68bf
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_gitlab.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_google.png b/android/app/src/main/res/drawable-hdpi/icon_google.png
new file mode 100644
index 00000000..c84a47c7
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_google.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_linkedin.png b/android/app/src/main/res/drawable-hdpi/icon_linkedin.png
new file mode 100644
index 00000000..93708032
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_linkedin.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_meteor.png b/android/app/src/main/res/drawable-hdpi/icon_meteor.png
new file mode 100644
index 00000000..1d793b13
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_meteor.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_twitter.png b/android/app/src/main/res/drawable-hdpi/icon_twitter.png
new file mode 100644
index 00000000..4b057edd
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_twitter.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/key.png b/android/app/src/main/res/drawable-hdpi/key.png
new file mode 100644
index 00000000..5f9471db
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/key.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/lock.png b/android/app/src/main/res/drawable-hdpi/lock.png
index 60f2bc36..68537368 100644
Binary files a/android/app/src/main/res/drawable-hdpi/lock.png and b/android/app/src/main/res/drawable-hdpi/lock.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/mail.png b/android/app/src/main/res/drawable-hdpi/mail.png
new file mode 100644
index 00000000..ff8b7ead
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/mail.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/mention.png b/android/app/src/main/res/drawable-hdpi/mention.png
index 637ac5dc..1049562e 100644
Binary files a/android/app/src/main/res/drawable-hdpi/mention.png and b/android/app/src/main/res/drawable-hdpi/mention.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/more.png b/android/app/src/main/res/drawable-hdpi/more.png
new file mode 100644
index 00000000..b92f0310
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/more.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/options.png b/android/app/src/main/res/drawable-hdpi/options.png
new file mode 100644
index 00000000..8e3bcc2c
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/options.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/subscription_hashtag.png b/android/app/src/main/res/drawable-hdpi/subscription_hashtag.png
deleted file mode 100644
index 76cf0a34..00000000
Binary files a/android/app/src/main/res/drawable-hdpi/subscription_hashtag.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/subscription_lock.png b/android/app/src/main/res/drawable-hdpi/subscription_lock.png
deleted file mode 100644
index 71ec7462..00000000
Binary files a/android/app/src/main/res/drawable-hdpi/subscription_lock.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/user.png b/android/app/src/main/res/drawable-hdpi/user.png
new file mode 100644
index 00000000..2c5ea6ed
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/user.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/eye.png b/android/app/src/main/res/drawable-mdpi/eye.png
new file mode 100644
index 00000000..34a12607
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/eye.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/eye_slash.png b/android/app/src/main/res/drawable-mdpi/eye_slash.png
new file mode 100644
index 00000000..621162a1
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/eye_slash.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/hashtag.png b/android/app/src/main/res/drawable-mdpi/hashtag.png
index 890f02c7..e4a79ec3 100644
Binary files a/android/app/src/main/res/drawable-mdpi/hashtag.png and b/android/app/src/main/res/drawable-mdpi/hashtag.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_facebook.png b/android/app/src/main/res/drawable-mdpi/icon_facebook.png
new file mode 100644
index 00000000..b362e2ff
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_facebook.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_github.png b/android/app/src/main/res/drawable-mdpi/icon_github.png
new file mode 100644
index 00000000..af76b3fd
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_github.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_gitlab.png b/android/app/src/main/res/drawable-mdpi/icon_gitlab.png
new file mode 100644
index 00000000..908f0f31
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_gitlab.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_google.png b/android/app/src/main/res/drawable-mdpi/icon_google.png
new file mode 100644
index 00000000..77677544
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_google.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_linkedin.png b/android/app/src/main/res/drawable-mdpi/icon_linkedin.png
new file mode 100644
index 00000000..29cbe97f
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_linkedin.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_meteor.png b/android/app/src/main/res/drawable-mdpi/icon_meteor.png
new file mode 100644
index 00000000..5489e031
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_meteor.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_twitter.png b/android/app/src/main/res/drawable-mdpi/icon_twitter.png
new file mode 100644
index 00000000..4570f1a5
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_twitter.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/key.png b/android/app/src/main/res/drawable-mdpi/key.png
new file mode 100644
index 00000000..b3132d59
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/key.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/lock.png b/android/app/src/main/res/drawable-mdpi/lock.png
index f0d911fd..a87c4495 100644
Binary files a/android/app/src/main/res/drawable-mdpi/lock.png and b/android/app/src/main/res/drawable-mdpi/lock.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/mail.png b/android/app/src/main/res/drawable-mdpi/mail.png
new file mode 100644
index 00000000..de70ea51
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/mail.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/mention.png b/android/app/src/main/res/drawable-mdpi/mention.png
index 4455b43e..d02eef25 100644
Binary files a/android/app/src/main/res/drawable-mdpi/mention.png and b/android/app/src/main/res/drawable-mdpi/mention.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/more.png b/android/app/src/main/res/drawable-mdpi/more.png
new file mode 100644
index 00000000..1423e44a
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/more.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/options.png b/android/app/src/main/res/drawable-mdpi/options.png
new file mode 100644
index 00000000..a2bd9ecd
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/options.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/subscription_hashtag.png b/android/app/src/main/res/drawable-mdpi/subscription_hashtag.png
deleted file mode 100644
index d3719dad..00000000
Binary files a/android/app/src/main/res/drawable-mdpi/subscription_hashtag.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/subscription_lock.png b/android/app/src/main/res/drawable-mdpi/subscription_lock.png
deleted file mode 100644
index e345bfdd..00000000
Binary files a/android/app/src/main/res/drawable-mdpi/subscription_lock.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/user.png b/android/app/src/main/res/drawable-mdpi/user.png
new file mode 100644
index 00000000..4ea8662c
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/user.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/eye.png b/android/app/src/main/res/drawable-xhdpi/eye.png
new file mode 100644
index 00000000..611c8aa0
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/eye.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/eye_slash.png b/android/app/src/main/res/drawable-xhdpi/eye_slash.png
new file mode 100644
index 00000000..6751bad0
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/eye_slash.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/hashtag.png b/android/app/src/main/res/drawable-xhdpi/hashtag.png
index 70a5b3f6..d76738e3 100644
Binary files a/android/app/src/main/res/drawable-xhdpi/hashtag.png and b/android/app/src/main/res/drawable-xhdpi/hashtag.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_facebook.png b/android/app/src/main/res/drawable-xhdpi/icon_facebook.png
new file mode 100644
index 00000000..e9543fd0
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_facebook.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_github.png b/android/app/src/main/res/drawable-xhdpi/icon_github.png
new file mode 100644
index 00000000..f92f531e
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_github.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_gitlab.png b/android/app/src/main/res/drawable-xhdpi/icon_gitlab.png
new file mode 100644
index 00000000..92a633c8
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_gitlab.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_google.png b/android/app/src/main/res/drawable-xhdpi/icon_google.png
new file mode 100644
index 00000000..4fb517cf
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_google.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_linkedin.png b/android/app/src/main/res/drawable-xhdpi/icon_linkedin.png
new file mode 100644
index 00000000..e96d63de
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_linkedin.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_meteor.png b/android/app/src/main/res/drawable-xhdpi/icon_meteor.png
new file mode 100644
index 00000000..25b8bc18
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_meteor.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_twitter.png b/android/app/src/main/res/drawable-xhdpi/icon_twitter.png
new file mode 100644
index 00000000..7f8cf479
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_twitter.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/key.png b/android/app/src/main/res/drawable-xhdpi/key.png
new file mode 100644
index 00000000..face74d6
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/key.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/lock.png b/android/app/src/main/res/drawable-xhdpi/lock.png
index 7522c451..ba072523 100644
Binary files a/android/app/src/main/res/drawable-xhdpi/lock.png and b/android/app/src/main/res/drawable-xhdpi/lock.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/mail.png b/android/app/src/main/res/drawable-xhdpi/mail.png
new file mode 100644
index 00000000..2425cfa0
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/mail.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/mention.png b/android/app/src/main/res/drawable-xhdpi/mention.png
index 1f51d42f..3d64c502 100644
Binary files a/android/app/src/main/res/drawable-xhdpi/mention.png and b/android/app/src/main/res/drawable-xhdpi/mention.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/more.png b/android/app/src/main/res/drawable-xhdpi/more.png
new file mode 100644
index 00000000..4d8111d8
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/more.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/options.png b/android/app/src/main/res/drawable-xhdpi/options.png
new file mode 100644
index 00000000..612d02be
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/options.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/subscription_hashtag.png b/android/app/src/main/res/drawable-xhdpi/subscription_hashtag.png
deleted file mode 100644
index e4a79ec3..00000000
Binary files a/android/app/src/main/res/drawable-xhdpi/subscription_hashtag.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/subscription_lock.png b/android/app/src/main/res/drawable-xhdpi/subscription_lock.png
deleted file mode 100644
index a87c4495..00000000
Binary files a/android/app/src/main/res/drawable-xhdpi/subscription_lock.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/user.png b/android/app/src/main/res/drawable-xhdpi/user.png
new file mode 100644
index 00000000..933f3b71
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/user.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/eye.png b/android/app/src/main/res/drawable-xxhdpi/eye.png
new file mode 100644
index 00000000..e8d2a62d
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/eye.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/eye_slash.png b/android/app/src/main/res/drawable-xxhdpi/eye_slash.png
new file mode 100644
index 00000000..ce3b4159
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/eye_slash.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/hashtag.png b/android/app/src/main/res/drawable-xxhdpi/hashtag.png
index 8017c826..e083190f 100644
Binary files a/android/app/src/main/res/drawable-xxhdpi/hashtag.png and b/android/app/src/main/res/drawable-xxhdpi/hashtag.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_facebook.png b/android/app/src/main/res/drawable-xxhdpi/icon_facebook.png
new file mode 100644
index 00000000..d6d01570
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_facebook.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_github.png b/android/app/src/main/res/drawable-xxhdpi/icon_github.png
new file mode 100644
index 00000000..de753329
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_github.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_gitlab.png b/android/app/src/main/res/drawable-xxhdpi/icon_gitlab.png
new file mode 100644
index 00000000..fb8211a7
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_gitlab.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_google.png b/android/app/src/main/res/drawable-xxhdpi/icon_google.png
new file mode 100644
index 00000000..02342b21
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_google.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_linkedin.png b/android/app/src/main/res/drawable-xxhdpi/icon_linkedin.png
new file mode 100644
index 00000000..674c9ef3
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_linkedin.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_meteor.png b/android/app/src/main/res/drawable-xxhdpi/icon_meteor.png
new file mode 100644
index 00000000..b7a0a845
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_meteor.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_twitter.png b/android/app/src/main/res/drawable-xxhdpi/icon_twitter.png
new file mode 100644
index 00000000..3faaeac1
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_twitter.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/key.png b/android/app/src/main/res/drawable-xxhdpi/key.png
new file mode 100644
index 00000000..11206129
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/key.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/lock.png b/android/app/src/main/res/drawable-xxhdpi/lock.png
index 72797f88..7b310acc 100644
Binary files a/android/app/src/main/res/drawable-xxhdpi/lock.png and b/android/app/src/main/res/drawable-xxhdpi/lock.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/mail.png b/android/app/src/main/res/drawable-xxhdpi/mail.png
new file mode 100644
index 00000000..3b9ad1b6
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/mail.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/mention.png b/android/app/src/main/res/drawable-xxhdpi/mention.png
index 29defc2c..3947ed67 100644
Binary files a/android/app/src/main/res/drawable-xxhdpi/mention.png and b/android/app/src/main/res/drawable-xxhdpi/mention.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/more.png b/android/app/src/main/res/drawable-xxhdpi/more.png
new file mode 100644
index 00000000..3b7cdb0b
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/more.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/options.png b/android/app/src/main/res/drawable-xxhdpi/options.png
new file mode 100644
index 00000000..2622837a
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/options.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/subscription_hashtag.png b/android/app/src/main/res/drawable-xxhdpi/subscription_hashtag.png
deleted file mode 100644
index 5d4be58c..00000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/subscription_hashtag.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/subscription_lock.png b/android/app/src/main/res/drawable-xxhdpi/subscription_lock.png
deleted file mode 100644
index 68537368..00000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/subscription_lock.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/user.png b/android/app/src/main/res/drawable-xxhdpi/user.png
new file mode 100644
index 00000000..c8960e2b
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/user.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/eye.png b/android/app/src/main/res/drawable-xxxhdpi/eye.png
new file mode 100644
index 00000000..a30e5d3a
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/eye.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/eye_slash.png b/android/app/src/main/res/drawable-xxxhdpi/eye_slash.png
new file mode 100644
index 00000000..9f7f41cc
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/eye_slash.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/hashtag.png b/android/app/src/main/res/drawable-xxxhdpi/hashtag.png
index 1a88b82f..c79e0c6b 100644
Binary files a/android/app/src/main/res/drawable-xxxhdpi/hashtag.png and b/android/app/src/main/res/drawable-xxxhdpi/hashtag.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_facebook.png b/android/app/src/main/res/drawable-xxxhdpi/icon_facebook.png
new file mode 100644
index 00000000..d19bdb23
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_facebook.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_github.png b/android/app/src/main/res/drawable-xxxhdpi/icon_github.png
new file mode 100644
index 00000000..61932967
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_github.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_gitlab.png b/android/app/src/main/res/drawable-xxxhdpi/icon_gitlab.png
new file mode 100644
index 00000000..01100b0f
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_gitlab.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_google.png b/android/app/src/main/res/drawable-xxxhdpi/icon_google.png
new file mode 100644
index 00000000..7693fdfc
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_google.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_linkedin.png b/android/app/src/main/res/drawable-xxxhdpi/icon_linkedin.png
new file mode 100644
index 00000000..677a22b7
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_linkedin.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_meteor.png b/android/app/src/main/res/drawable-xxxhdpi/icon_meteor.png
new file mode 100644
index 00000000..eeb52443
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_meteor.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_twitter.png b/android/app/src/main/res/drawable-xxxhdpi/icon_twitter.png
new file mode 100644
index 00000000..fda06f99
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_twitter.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/key.png b/android/app/src/main/res/drawable-xxxhdpi/key.png
new file mode 100644
index 00000000..9879f426
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/key.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/lock.png b/android/app/src/main/res/drawable-xxxhdpi/lock.png
index e4931fe5..0673b6aa 100644
Binary files a/android/app/src/main/res/drawable-xxxhdpi/lock.png and b/android/app/src/main/res/drawable-xxxhdpi/lock.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/mail.png b/android/app/src/main/res/drawable-xxxhdpi/mail.png
new file mode 100644
index 00000000..780afd4d
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/mail.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/mention.png b/android/app/src/main/res/drawable-xxxhdpi/mention.png
index 14b2374d..5ef79b4b 100644
Binary files a/android/app/src/main/res/drawable-xxxhdpi/mention.png and b/android/app/src/main/res/drawable-xxxhdpi/mention.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/more.png b/android/app/src/main/res/drawable-xxxhdpi/more.png
new file mode 100644
index 00000000..7c8ab1ce
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/more.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/options.png b/android/app/src/main/res/drawable-xxxhdpi/options.png
new file mode 100644
index 00000000..f9a4f021
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/options.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/subscription_hashtag.png b/android/app/src/main/res/drawable-xxxhdpi/subscription_hashtag.png
deleted file mode 100644
index d76738e3..00000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/subscription_hashtag.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/subscription_lock.png b/android/app/src/main/res/drawable-xxxhdpi/subscription_lock.png
deleted file mode 100644
index ba072523..00000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/subscription_lock.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/user.png b/android/app/src/main/res/drawable-xxxhdpi/user.png
new file mode 100644
index 00000000..b4374ec0
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/user.png differ
diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index e7b82839..7c191903 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -16,22 +16,14 @@ export const LOGIN = createRequestTypes('LOGIN', [
'SUBMIT',
'REGISTER_SUBMIT',
'REGISTER_REQUEST',
- 'REGISTER_SUCCESS',
- 'REGISTER_INCOMPLETE',
'SET_USERNAME_SUBMIT',
'SET_USERNAME_REQUEST',
'SET_USERNAME_SUCCESS',
- 'OPEN',
- 'CLOSE',
'SET_SERVICES',
- 'REMOVE_SERVICES',
'SET_PREFERENCE',
'SET_SORT_PREFERENCE'
]);
-export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
- ...defaultTypes,
- 'INIT'
-]);
+export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD');
export const USER = createRequestTypes('USER', ['SET']);
export const ROOMS = createRequestTypes('ROOMS', [
...defaultTypes,
diff --git a/app/actions/login.js b/app/actions/login.js
index 529039ce..cc5c1715 100644
--- a/app/actions/login.js
+++ b/app/actions/login.js
@@ -19,23 +19,13 @@ export function registerSubmit(credentials) {
credentials
};
}
+
export function registerRequest(credentials) {
return {
type: types.LOGIN.REGISTER_REQUEST,
credentials
};
}
-export function registerSuccess(credentials) {
- return {
- type: types.LOGIN.REGISTER_SUCCESS,
- credentials
- };
-}
-export function registerIncomplete() {
- return {
- type: types.LOGIN.REGISTER_INCOMPLETE
- };
-}
export function setUsernameSubmit(credentials) {
return {
@@ -127,18 +117,6 @@ export function setUser(action) {
};
}
-export function open() {
- return {
- type: types.LOGIN.OPEN
- };
-}
-
-export function close() {
- return {
- type: types.LOGIN.CLOSE
- };
-}
-
export function setLoginServices(data) {
return {
type: types.LOGIN.SET_SERVICES,
@@ -146,12 +124,6 @@ export function setLoginServices(data) {
};
}
-export function removeLoginServices() {
- return {
- type: types.LOGIN.REMOVE_SERVICES
- };
-}
-
export function setPreference(preference) {
return {
type: types.LOGIN.SET_PREFERENCE,
diff --git a/app/constants/colors.js b/app/constants/colors.js
index e7545cca..be30581e 100644
--- a/app/constants/colors.js
+++ b/app/constants/colors.js
@@ -1,5 +1,5 @@
export const COLOR_DANGER = '#f5455c';
-export const COLOR_BUTTON_PRIMARY = '#2D6AEA';
+export const COLOR_BUTTON_PRIMARY = '#1d74f5';
export const COLOR_TEXT = '#292E35';
export const COLOR_SEPARATOR = '#CBCED1';
export const STATUS_COLORS = {
diff --git a/app/constants/headerOptions.js b/app/constants/headerOptions.js
new file mode 100644
index 00000000..a02384fc
--- /dev/null
+++ b/app/constants/headerOptions.js
@@ -0,0 +1,60 @@
+import { Platform } from 'react-native';
+
+export const DARK_HEADER = {
+ statusBar: {
+ backgroundColor: '#2F343D',
+ style: 'light'
+ },
+ topBar: {
+ backButton: {
+ color: '#fff'
+ },
+ background: {
+ color: '#2F343D'
+ },
+ title: {
+ color: '#FFF'
+ },
+ leftButtonStyle: {
+ color: '#FFF'
+ },
+ rightButtonStyle: {
+ color: '#FFF'
+ }
+ }
+};
+
+export const LIGHT_HEADER = {
+ statusBar: {
+ backgroundColor: '#FFF',
+ style: 'dark'
+ },
+ topBar: {
+ backButton: {
+ color: '#1d74f5'
+ },
+ background: {
+ color: undefined
+ },
+ title: {
+ color: '#0C0D0F'
+ },
+ leftButtonStyle: {
+ color: '#1d74f5'
+ },
+ rightButtonStyle: {
+ color: '#1d74f5'
+ }
+ }
+};
+
+export const DEFAULT_HEADER = {
+ ...Platform.select({
+ ios: {
+ ...LIGHT_HEADER
+ },
+ android: {
+ ...DARK_HEADER
+ }
+ })
+};
diff --git a/app/constants/settings.js b/app/constants/settings.js
index ec991626..12277bf5 100644
--- a/app/constants/settings.js
+++ b/app/constants/settings.js
@@ -8,33 +8,9 @@ export default {
Accounts_NamePlaceholder: {
type: 'valueAsString'
},
- Accounts_OAuth_Facebook: {
- type: 'valueAsBoolean'
- },
- Accounts_OAuth_Github: {
- type: 'valueAsBoolean'
- },
- Accounts_OAuth_Gitlab: {
- type: 'valueAsBoolean'
- },
- Accounts_OAuth_Google: {
- type: 'valueAsBoolean'
- },
- Accounts_OAuth_Linkedin: {
- type: 'valueAsBoolean'
- },
- Accounts_OAuth_Meteor: {
- type: 'valueAsBoolean'
- },
- Accounts_OAuth_Twitter: {
- type: 'valueAsBoolean'
- },
Accounts_PasswordPlaceholder: {
type: 'valueAsString'
},
- Accounts_RepeatPasswordPlaceholder: {
- type: 'valueAsString'
- },
CROWD_Enable: {
type: 'valueAsBoolean'
},
@@ -84,4 +60,4 @@ export default {
type: 'valueAsBoolean'
}
};
-export const settingsUpdatedAt = new Date('2018-09-10');
+export const settingsUpdatedAt = new Date('2018-11-14');
diff --git a/app/containers/Button/index.js b/app/containers/Button/index.js
index f1f3ade3..b632abc5 100644
--- a/app/containers/Button/index.js
+++ b/app/containers/Button/index.js
@@ -1,32 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {
- StyleSheet, View, Text, Platform, ActivityIndicator
-} from 'react-native';
+import { StyleSheet, Text, ActivityIndicator } from 'react-native';
+import { RectButton } from 'react-native-gesture-handler';
-import { COLOR_BUTTON_PRIMARY, COLOR_TEXT } from '../../constants/colors';
-import Touch from '../../utils/touch';
-import { scale, moderateScale, verticalScale } from '../../utils/scaling';
+import { COLOR_BUTTON_PRIMARY } from '../../constants/colors';
+import sharedStyles from '../../views/Styles';
const colors = {
background_primary: COLOR_BUTTON_PRIMARY,
background_secondary: 'white',
text_color_primary: 'white',
- text_color_secondary: COLOR_TEXT
+ text_color_secondary: COLOR_BUTTON_PRIMARY
};
/* eslint-disable react-native/no-unused-styles */
const styles = StyleSheet.create({
container: {
- paddingHorizontal: scale(15),
+ paddingHorizontal: 15,
justifyContent: 'center',
- height: scale(48)
+ height: 48,
+ borderRadius: 2,
+ marginBottom: 10
},
text: {
- fontSize: moderateScale(18),
- height: verticalScale(20),
- lineHeight: verticalScale(20),
+ fontSize: 18,
textAlign: 'center',
fontWeight: '500'
},
@@ -36,30 +34,25 @@ const styles = StyleSheet.create({
background_secondary: {
backgroundColor: colors.background_secondary
},
- text_color_primary: {
+ text_primary: {
+ ...sharedStyles.textMedium,
color: colors.text_color_primary
},
- text_color_secondary: {
+ text_secondary: {
+ ...sharedStyles.textBold,
color: colors.text_color_secondary
},
- margin: {
- marginBottom: verticalScale(10)
- },
disabled: {
- opacity: 0.5
- },
- border: {
- borderRadius: scale(2)
+ backgroundColor: '#e1e5e8'
}
});
export default class Button extends React.PureComponent {
static propTypes = {
- title: PropTypes.string,
+ title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
type: PropTypes.string,
onPress: PropTypes.func,
disabled: PropTypes.bool,
- margin: PropTypes.any,
backgroundColor: PropTypes.string,
loading: PropTypes.bool
}
@@ -74,32 +67,26 @@ export default class Button extends React.PureComponent {
render() {
const {
- title, type, onPress, disabled, margin, backgroundColor, loading, ...otherProps
+ title, type, onPress, disabled, backgroundColor, loading, ...otherProps
} = this.props;
return (
-
-
- {
- loading
- ?
- : {title}
- }
-
-
+ {
+ loading
+ ?
+ : {title}
+ }
+
);
}
}
diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js
index bec5be29..975606dc 100644
--- a/app/containers/Sidebar.js
+++ b/app/containers/Sidebar.js
@@ -85,7 +85,7 @@ const styles = StyleSheet.create({
const keyExtractor = item => item.id;
@connect(state => ({
- server: state.server.server,
+ Site_Name: state.settings.Site_Name,
user: {
id: state.login.user && state.login.user.id,
language: state.login.user && state.login.user.language,
@@ -101,7 +101,7 @@ export default class Sidebar extends Component {
static propTypes = {
baseUrl: PropTypes.string,
componentId: PropTypes.string,
- server: PropTypes.string.isRequired,
+ Site_Name: PropTypes.string.isRequired,
user: PropTypes.object,
logout: PropTypes.func.isRequired,
appStart: PropTypes.func
@@ -289,7 +289,7 @@ export default class Sidebar extends Component {
render() {
const { showStatus } = this.state;
- const { user, server, baseUrl } = this.props;
+ const { user, Site_Name, baseUrl } = this.props;
if (!user) {
return null;
@@ -315,7 +315,7 @@ export default class Sidebar extends Component {
{user.username}
- {server}
+ {Site_Name}
-
- iconLeft = (name) => {
- const { testID } = this.props;
- return this.icon({
- name,
- onPress: null,
- style: { left: 0 },
- testID: testID ? `${ testID }-icon-left` : null
- });
+ get iconLeft() {
+ const { testID, iconLeft } = this.props;
+ return (
+
+ );
}
- iconPassword = (name) => {
+ get iconPassword() {
+ const { showPassword } = this.state;
const { testID } = this.props;
- return this.icon({
- name,
- onPress: () => this.tooglePassword(),
- style: { right: 0 },
- testID: testID ? `${ testID }-icon-right` : null
- });
+ return (
+
+
+
+ );
}
tooglePassword = () => {
- const { showPassword } = this.state;
- this.setState({ showPassword: !showPassword });
+ this.setState(prevState => ({ showPassword: !prevState.showPassword }));
}
render() {
+ const { showPassword } = this.state;
const {
label, error, secureTextEntry, containerStyle, inputRef, iconLeft, inputStyle, testID, placeholder, ...inputProps
} = this.props;
- const { showPassword } = this.state;
return (
- {label ? {label} : null }
+ {label ? {label} : null}
- {iconLeft ? this.iconLeft(iconLeft) : null}
- {secureTextEntry ? this.iconPassword(showPassword ? 'eye-off' : 'eye') : null}
+ {iconLeft ? this.iconLeft : null}
+ {secureTextEntry ? this.iconPassword : null}
{error.error ? {error.reason} : null}
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
index 9f67b428..74563eed 100644
--- a/app/i18n/locales/en.js
+++ b/app/i18n/locales/en.js
@@ -121,14 +121,16 @@ export default {
Choose_from_library: 'Choose from library',
Code: 'Code',
Collaborative: 'Collaborative',
+ Confirm: 'Confirm',
Connect: 'Connect',
Connect_to_a_server: 'Connect to a server',
Connected: 'Connected',
Connecting: 'Connecting...',
+ Continue_with: 'Continue with',
Copied_to_clipboard: 'Copied to clipboard!',
Copy_Message: 'Copy Message',
Copy_Permalink: 'Copy Permalink',
- Create_account: 'Create account',
+ Create_account: 'Create an account',
Create_Channel: 'Create Channel',
Create_a_new_workspace: 'Create a new workspace',
Create: 'Create',
@@ -140,12 +142,14 @@ export default {
Description: 'Description',
Disable_notifications: 'Disable notifications',
Direct_Messages: 'Direct Messages',
+ Dont_Have_An_Account: 'Don\'t have an account?',
Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?',
edit: 'edit',
erasing_room: 'erasing room',
Edit: 'Edit',
Email_or_password_field_is_empty: 'Email or password field is empty',
Email: 'Email',
+ email: 'e-mail',
Enable_notifications: 'Enable notifications',
Everyone_can_access_this_channel: 'Everyone can access this channel',
Error_uploading: 'Error uploading',
@@ -161,7 +165,6 @@ export default {
Group_by_type: 'Group by type',
Has_joined_the_channel: 'Has joined the channel',
Has_left_the_channel: 'Has left the channel',
- I_have_an_account: 'I have an account',
Invisible: 'Invisible',
Invite: 'Invite',
is_a_valid_RocketChat_instance: 'is a valid Rocket.Chat instance',
@@ -175,8 +178,11 @@ export default {
Leave_channel: 'Leave channel',
leaving_room: 'leaving room',
leave: 'leave',
+ Legal: 'Legal',
Livechat: 'Livechat',
Login: 'Login',
+ Login_error: 'Your credentials were rejected! Please try again.',
+ Login_with: 'Login with',
Logout: 'Logout',
Members: 'Members',
Mentioned_Messages: 'Mentioned Messages',
@@ -197,7 +203,6 @@ export default {
N_users: '{{n}} users',
name: 'name',
Name: 'Name',
- New_in_RocketChat_question_mark: 'New in Rocket.Chat?',
New_Message: 'New Message',
New_Password: 'New Password',
New_Server: 'New Server',
@@ -222,7 +227,6 @@ export default {
Only_authorized_users_can_write_new_messages: 'Only authorized users can write new messages',
Open_emoji_selector: 'Open emoji selector',
Open_Source_Communication: 'Open Source Communication',
- Or_continue_using_social_accounts: 'Or continue using social accounts',
Password: 'Password',
Permalink_copied_to_clipboard: 'Permalink copied to clipboard!',
Pin: 'Pin',
@@ -277,6 +281,7 @@ export default {
Send_message: 'Send message',
Server: 'Server',
Servers: 'Servers',
+ Set_username_subtitle: 'The username is used to allow others to mention you in messages',
Settings: 'Settings',
Settings_succesfully_changed: 'Settings succesfully changed!',
Share_Message: 'Share Message',
@@ -308,6 +313,7 @@ export default {
topic: 'topic',
Topic: 'Topic',
Try_again: 'Try again',
+ Two_Factor_Authentication: 'Two-factor Authentication',
Type_the_channel_name_here: 'Type the channel name here',
unarchive: 'unarchive',
UNARCHIVE: 'UNARCHIVE',
@@ -330,13 +336,13 @@ export default {
User_was_set_role_by_: '{{user}} was set {{role}} by {{userBy}}',
Username_is_empty: 'Username is empty',
Username: 'Username',
+ Username_or_email: 'Username or email',
Validating: 'Validating',
Video_call: 'Video call',
Voice_call: 'Voice call',
Welcome: 'Welcome',
- Welcome_title_pt_1: 'Prepare to take off with',
- Welcome_title_pt_2: 'the ultimate chat platform',
Welcome_to_RocketChat: 'Welcome to Rocket.Chat',
+ Whats_your_2fa: 'What\'s your 2FA code?',
Yes_action_it: 'Yes, {{action}} it!',
Yesterday: 'Yesterday',
You_are_in_preview_mode: 'You are in preview mode',
diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js
index bce2d48e..3232282b 100644
--- a/app/i18n/locales/pt-BR.js
+++ b/app/i18n/locales/pt-BR.js
@@ -128,10 +128,12 @@ export default {
Choose_from_library: 'Escolha da biblioteca',
Code: 'Código',
Collaborative: 'Colaborativo',
+ Confirm: 'Confirmar',
Connect: 'Conectar',
Connect_to_a_server: 'Conectar a um servidor',
Connected: 'Conectado',
Connecting: 'Conectando...',
+ Continue_with: 'Entrar com',
Copied_to_clipboard: 'Copiado para a área de transferência!',
Copy_Message: 'Copiar Mensagem',
Copy_Permalink: 'Copiar Link-Permanente',
@@ -147,12 +149,14 @@ export default {
Description: 'Descrição',
Disable_notifications: 'Desabilitar notificações',
Direct_Messages: 'Mensagens Diretas',
+ Dont_Have_An_Account: 'Não tem uma conta?',
Do_you_really_want_to_key_this_room_question_mark: 'Você quer realmente {{key}} esta sala?',
edit: 'editar',
erasing_room: 'apagando sala',
Edit: 'Editar',
Email_or_password_field_is_empty: 'Email ou senha estão vazios',
Email: 'Email',
+ email: 'e-mail',
Enable_notifications: 'Habilitar notificações',
Everyone_can_access_this_channel: 'Todos podem acessar este canal',
Error_uploading: 'Erro subindo',
@@ -168,7 +172,6 @@ export default {
Group_by_type: 'Grupos por tipo',
Has_joined_the_channel: 'Entrou no canal',
Has_left_the_channel: 'Saiu da conversa',
- I_have_an_account: 'Eu tenho uma conta',
Invisible: 'Invisível',
Invite: 'Convidar',
is_typing: 'está digitando',
@@ -180,8 +183,11 @@ export default {
Leave_channel: 'Sair do canal',
leaving_room: 'saindo do canal',
leave: 'sair',
+ Legal: 'Legal',
Livechat: 'Livechat',
Login: 'Entrar',
+ Login_error: 'Suas credenciais foram rejeitadas. Tente novamente por favor!',
+ Login_with: 'Login with',
Logout: 'Sair',
Members: 'Membros',
Mentioned_Messages: 'Mensagens mencionadas',
@@ -224,7 +230,6 @@ export default {
Only_authorized_users_can_write_new_messages: 'Somente usuários autorizados podem escrever novas mensagens',
Open_emoji_selector: 'Abrir seletor de emoji',
Open_Source_Communication: 'Comunicação Open Source',
- Or_continue_using_social_accounts: 'Ou continue usando redes sociais',
Password: 'Senha',
Permalink_copied_to_clipboard: 'Link-permanente copiado para a área de transferência!',
Pin: 'Fixar',
@@ -278,6 +283,7 @@ export default {
Send_audio_message: 'Enviar mensagem de áudio',
Send_message: 'Enviar mensagem',
Server: 'Servidor',
+ Set_username_subtitle: 'O usuário é utilizado para permitir que você seja mencionado em mensagens',
Settings: 'Configurações',
Settings_succesfully_changed: 'Configurações salvas com sucesso!',
Share_Message: 'Compartilhar Mensagem',
@@ -306,6 +312,7 @@ export default {
topic: 'tópico',
Topic: 'Tópico',
Try_again: 'Tentar novamente',
+ Two_Factor_Authentication: 'Autenticação de dois fatores',
Type_the_channel_name_here: 'Digite o nome do canal',
unarchive: 'desarquivar',
UNARCHIVE: 'DESARQUIVAR',
@@ -328,12 +335,12 @@ export default {
User_was_set_role_by_: '{{user}} foi definido como {{role}} por {{userBy}}',
Username_is_empty: 'Usuário está vazio',
Username: 'Usuário',
+ Username_or_email: 'Usuário ou email',
Video_call: 'Chamada de vídeo',
Voice_call: 'Chamada de voz',
Welcome: 'Bem vindo',
- Welcome_title_pt_1: 'Prepare-se para decolar com',
- Welcome_title_pt_2: 'a melhor plataforma de chat',
Welcome_to_RocketChat: 'Bem vindo ao Rocket.Chat',
+ Whats_your_2fa: 'Qual seu código de autenticação?',
Yes_action_it: 'Sim, {{action}}!',
Yesterday: 'Ontem',
You_are_in_preview_mode: 'Está é uma prévia do canal',
diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js
index b7bf873a..7fb8a0cc 100644
--- a/app/i18n/locales/ru.js
+++ b/app/i18n/locales/ru.js
@@ -200,7 +200,6 @@ export default {
Online: 'Онлайн',
Only_authorized_users_can_write_new_messages: 'Только авторизованные пользователи могут писать новые сообщения',
Open_emoji_selector: 'Открыть селектор emoji',
- Or_continue_using_social_accounts: 'Или продолжить, используя социальные учетные записи',
Password: 'Пароль',
Permalink_copied_to_clipboard: 'Постоянная ссылка скопирована в буфер обмена!',
Pin: 'Прикрепить сообщение',
@@ -305,8 +304,6 @@ export default {
Video_call: 'Видеозвонок',
Voice_call: 'Голосовой вызов',
Welcome: 'Добро пожаловать',
- Welcome_title_pt_1: 'Приготовьтесь к взлету с',
- Welcome_title_pt_2: 'передовой чат-платформой',
Yes_action_it: 'Да, {{action}} это!',
Yesterday: 'Вчера',
You_are_in_preview_mode: 'Вы находитесь в режиме предварительного просмотра',
diff --git a/app/i18n/locales/zh-CN.js b/app/i18n/locales/zh-CN.js
index 355435e3..a5352d3c 100644
--- a/app/i18n/locales/zh-CN.js
+++ b/app/i18n/locales/zh-CN.js
@@ -222,7 +222,6 @@ export default {
Only_authorized_users_can_write_new_messages: '只有经过授权的用户才能写新消息',
Open_emoji_selector: '打开emoji选择器',
Open_Source_Communication: '开源沟通',
- Or_continue_using_social_accounts: '或者继续使用社交账号',
Password: '密码',
Permalink_copied_to_clipboard: '永久链接已复制到剪贴板!',
Pin: '订住',
@@ -333,8 +332,6 @@ export default {
Video_call: '视频电话',
Voice_call: '语音电话',
Welcome: '欢迎',
- Welcome_title_pt_1: '准备起飞',
- Welcome_title_pt_2: '终极聊天平台',
Welcome_to_RocketChat: '欢迎来到 Rocket.Chat',
Yes_action_it: '是的,{{action}}它!',
Yesterday: '昨天',
diff --git a/app/index.js b/app/index.js
index 4acf28f3..c6cb8f6f 100644
--- a/app/index.js
+++ b/app/index.js
@@ -1,6 +1,8 @@
import { Component } from 'react';
-import { Linking, Platform, Dimensions } from 'react-native';
+import { Linking, Platform } from 'react-native';
import { Navigation } from 'react-native-navigation';
+import { Provider } from 'react-redux';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import store from './lib/createStore';
import { appInit } from './actions';
@@ -10,8 +12,6 @@ import { deepLinkingOpen } from './actions/deepLinking';
import parseQuery from './lib/methods/helpers/parseQuery';
import { initializePushNotifications } from './push';
-const isAndroid = () => Platform.OS === 'android';
-
const startLogged = () => {
Navigation.setRoot({
root: {
@@ -57,6 +57,30 @@ const startNotLogged = () => {
});
};
+let SetUsernameView = null;
+const startSetUsername = () => {
+ if (SetUsernameView == null) {
+ SetUsernameView = require('./views/SetUsernameView').default;
+ Navigation.registerComponentWithRedux('SetUsernameView', () => gestureHandlerRootHOC(SetUsernameView), Provider, store);
+ }
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [{
+ component: {
+ name: 'SetUsernameView'
+ }
+ }],
+ options: {
+ layout: {
+ orientation: ['portrait']
+ }
+ }
+ }
+ }
+ });
+};
+
const handleOpenURL = ({ url }) => {
if (url) {
url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
@@ -83,15 +107,17 @@ export default class App extends Component {
Navigation.setDefaultOptions({
topBar: {
backButton: {
- icon: { uri: 'back', scale: Dimensions.get('window').scale }
+ showTitle: false
+ },
+ leftButtonStyle: {
+ color: '#FFF'
+ },
+ rightButtonStyle: {
+ color: '#FFF'
},
title: {
- color: isAndroid() ? '#FFF' : undefined
- },
- background: {
- color: isAndroid() ? '#2F343D' : undefined
- },
- buttonColor: '#FFF'
+ fontFamily: Platform.OS === 'ios' ? 'System' : 'sans-serif-medium'
+ }
},
sideMenu: {
left: {
@@ -121,6 +147,8 @@ export default class App extends Component {
startNotLogged();
} else if (root === 'inside') {
startLogged();
+ } else if (root === 'setUsername') {
+ startSetUsername();
}
}
}
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 92c42788..5cee9833 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -11,7 +11,7 @@ import log from '../utils/log';
// import * as actions from '../actions';
import {
- setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure, logout
+ setUser, setLoginServices, loginRequest, loginSuccess, loginFailure, logout
} from '../actions/login';
import { disconnect, connectSuccess } from '../actions/connect';
import { setActiveUser } from '../actions/activeUsers';
@@ -125,18 +125,34 @@ const RocketChat = {
}
},
async loginSuccess(user) {
- try {
- if (!user) {
- const { user: u } = reduxStore.getState().login;
- user = Object.assign({}, u);
- }
+ if (!user) {
+ const { user: u } = reduxStore.getState().login;
+ user = Object.assign({}, u);
+ }
- // TODO: one api call
- // call /me only one time
+ // TODO: one api call
+ // call /me only one time
+ try {
if (!user.username) {
- const me = await SDK.api.get('me');
+ // get me from api
+ let me = await SDK.api.get('me');
+ // if server didn't found username
+ if (!me.username) {
+ // search username from credentials (sent during registerSubmit)
+ const { username } = reduxStore.getState().login.credentials;
+ if (username) {
+ // set username
+ await RocketChat.setUsername({ username });
+ me = { ...me, username };
+ }
+ }
user = { ...user, ...me };
}
+ } catch (e) {
+ log('SDK.loginSuccess set username', e);
+ }
+
+ try {
if (user.username) {
const userInfo = await SDK.api.get('users.info', { userId: user.id });
user = { ...user, ...userInfo.user };
@@ -362,28 +378,6 @@ const RocketChat = {
}
}));
- SDK.driver.on('meteor_accounts_loginServiceConfiguration', (error, ddpMessage) => {
- if (ddpMessage.msg === 'added') {
- this.loginServices = this.loginServices || {};
- if (this.loginServiceTimer) {
- clearTimeout(this.loginServiceTimer);
- this.loginServiceTimer = null;
- }
- this.loginServiceTimer = setTimeout(() => {
- reduxStore.dispatch(setLoginServices(this.loginServices));
- this.loginServiceTimer = null;
- return this.loginServices = {};
- }, 1000);
- this.loginServices[ddpMessage.fields.service] = { ...ddpMessage.fields };
- delete this.loginServices[ddpMessage.fields.service].service;
- } else if (ddpMessage.msg === 'removed') {
- if (this.loginServiceTimer) {
- clearTimeout(this.loginServiceTimer);
- }
- this.loginServiceTimer = setTimeout(() => reduxStore.dispatch(removeLoginServices()), 1000);
- }
- });
-
SDK.driver.on('rocketchat_roles', protectedFunction((error, ddpMessage) => {
this.roles = this.roles || {};
@@ -432,8 +426,8 @@ const RocketChat = {
return call('registerUser', credentials);
},
- setUsername({ credentials }) {
- return call('setUsername', credentials.username);
+ setUsername({ username }) {
+ return call('setUsername', username);
},
forgotPassword(email) {
@@ -622,7 +616,7 @@ const RocketChat = {
return setting;
});
},
- _filterSettings: settings => settings.filter(setting => defaultSettings[setting._id] && setting.value),
+ _filterSettings: settings => settings.filter(setting => defaultSettings[setting._id] && (setting.value || setting.valueAsString || setting.valueAsNumber || setting.valueAsBoolean)),
parseEmojis: emojis => emojis.reduce((ret, item) => {
ret[item.name] = item.extension;
item.aliases.forEach((alias) => {
@@ -805,6 +799,30 @@ const RocketChat = {
} catch (error) {
console.warn(error);
}
+ },
+ async getLoginServices(server) {
+ try {
+ let loginServicesFilter = [];
+ const loginServicesResult = await fetch(`${ server }/api/v1/settings.oauth`).then(response => response.json());
+ // TODO: remove this after SAML and custom oauth
+ const availableOAuth = ['facebook', 'github', 'gitlab', 'google', 'linkedin', 'meteor-developer', 'twitter'];
+ if (loginServicesResult.success && loginServicesResult.services.length > 0) {
+ const { services } = loginServicesResult;
+ loginServicesFilter = services.filter(item => availableOAuth.includes(item.name));
+ const loginServicesReducer = loginServicesFilter.reduce((ret, item) => {
+ ret[item.name] = item;
+ return ret;
+ }, {});
+ reduxStore.dispatch(setLoginServices(loginServicesReducer));
+ }
+ return Promise.resolve(loginServicesFilter.length);
+ } catch (error) {
+ console.warn(error);
+ return Promise.reject();
+ }
+ },
+ getUsernameSuggestion() {
+ return SDK.driver.asyncCall('getUsernameSuggestion');
}
};
diff --git a/app/presentation/RoomItem.js b/app/presentation/RoomItem.js
index 3e39558f..6c16105b 100644
--- a/app/presentation/RoomItem.js
+++ b/app/presentation/RoomItem.js
@@ -215,7 +215,7 @@ export default class RoomItem extends React.Component {
if (type === 'd') {
return ;
}
- return ;
+ return ;
}
formatDate = date => moment(date).calendar(null, {
diff --git a/app/reducers/login.js b/app/reducers/login.js
index cf4d2ff7..ef222d06 100644
--- a/app/reducers/login.js
+++ b/app/reducers/login.js
@@ -3,11 +3,11 @@ import * as types from '../actions/actionsTypes';
const initialState = {
isAuthenticated: false,
isFetching: false,
- isRegistering: false,
token: '',
user: {},
error: '',
- services: {}
+ services: {},
+ credentials: {}
};
export default function login(state = initialState, action) {
@@ -19,7 +19,6 @@ export default function login(state = initialState, action) {
...state,
isFetching: true,
isAuthenticated: false,
- isRegistering: false,
failure: false,
error: ''
};
@@ -34,7 +33,8 @@ export default function login(state = initialState, action) {
},
token: action.user.token,
failure: false,
- error: ''
+ error: '',
+ credentials: {}
};
case types.LOGIN.FAILURE:
return {
@@ -61,37 +61,29 @@ export default function login(state = initialState, action) {
return {
...state,
isFetching: true,
- isAuthenticated: false,
- isRegistering: true,
failure: false,
- error: ''
+ error: '',
+ credentials: action.credentials
};
case types.LOGIN.REGISTER_SUCCESS:
return {
...state,
isFetching: false,
- isAuthenticated: false,
failure: false,
- error: ''
+ error: '',
+ credentials: {}
};
case types.LOGIN.SET_USERNAME_SUBMIT:
return {
...state,
- isFetching: true
+ isFetching: true,
+ credentials: action.credentials
};
case types.LOGIN.SET_USERNAME_SUCCESS:
return {
...state,
- isFetching: false,
- isRegistering: false
+ isFetching: false
};
- case types.LOGIN.REGISTER_INCOMPLETE:
- return {
- ...state,
- isRegistering: true
- };
- case types.FORGOT_PASSWORD.INIT:
- return initialState;
case types.FORGOT_PASSWORD.REQUEST:
return {
...state,
@@ -128,11 +120,6 @@ export default function login(state = initialState, action) {
...action.data
}
};
- case types.LOGIN.REMOVE_SERVICES:
- return {
- ...state,
- services: {}
- };
case types.LOGIN.SET_PREFERENCE:
return {
...state,
diff --git a/app/sagas/login.js b/app/sagas/login.js
index b1011158..b5981cf3 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -1,7 +1,7 @@
import { AsyncStorage } from 'react-native';
import { delay } from 'redux-saga';
import {
- put, call, take, takeLatest, select, all
+ put, call, takeLatest, select, all
} from 'redux-saga/effects';
import { Navigation } from 'react-native-navigation';
@@ -9,15 +9,8 @@ import * as types from '../actions/actionsTypes';
import { appStart } from '../actions';
import { serverFinishAdd } from '../actions/server';
import {
- // loginRequest,
- // loginSubmit,
registerRequest,
- registerIncomplete,
- // loginSuccess,
loginFailure,
- // logout,
- // setToken,
- registerSuccess,
setUsernameRequest,
setUsernameSuccess,
forgotPasswordSuccess,
@@ -29,7 +22,6 @@ import I18n from '../i18n';
const getUser = state => state.login.user;
const getServer = state => state.server.server;
-const getIsConnected = state => state.meteor.connected;
const loginCall = args => RocketChat.loginWithPassword(args);
const registerCall = args => RocketChat.register(args);
@@ -43,16 +35,16 @@ const handleLoginSuccess = function* handleLoginSuccess() {
const user = yield select(getUser);
const adding = yield select(state => state.server.adding);
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
- if (!user.username || user.isRegistering) {
- yield put(registerIncomplete());
+
+ if (!user.username) {
+ return yield put(appStart('setUsername'));
+ }
+
+ if (adding) {
+ yield put(serverFinishAdd());
+ yield Navigation.dismissAllModals();
} else {
- yield delay(300);
- if (adding) {
- yield put(serverFinishAdd());
- yield Navigation.dismissAllModals();
- } else {
- yield put(appStart('inside'));
- }
+ yield put(appStart('inside'));
}
} catch (e) {
log('handleLoginSuccess', e);
@@ -66,14 +58,6 @@ const handleRegisterSubmit = function* handleRegisterSubmit({ credentials }) {
const handleRegisterRequest = function* handleRegisterRequest({ credentials }) {
try {
yield call(registerCall, { credentials });
- yield put(registerSuccess(credentials));
- } catch (err) {
- yield put(loginFailure(err));
- }
-};
-
-const handleRegisterSuccess = function* handleRegisterSuccess({ credentials }) {
- try {
yield call(loginCall, {
username: credentials.email,
password: credentials.pass
@@ -89,7 +73,7 @@ const handleSetUsernameSubmit = function* handleSetUsernameSubmit({ credentials
const handleSetUsernameRequest = function* handleSetUsernameRequest({ credentials }) {
try {
- yield call(setUsernameCall, { credentials });
+ yield call(setUsernameCall, credentials);
yield put(setUsernameSuccess());
yield call(loginSuccessCall);
} catch (err) {
@@ -109,13 +93,6 @@ const handleLogout = function* handleLogout() {
}
};
-const handleRegisterIncomplete = function* handleRegisterIncomplete() {
- const server = yield select(state => state.server);
- if (!server.adding) {
- yield put(appStart('outside'));
- }
-};
-
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
try {
yield call(forgotPasswordCall, email);
@@ -125,22 +102,6 @@ const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ emai
}
};
-const watchLoginOpen = function* watchLoginOpen() {
- try {
- const isConnected = yield select(getIsConnected);
- if (!isConnected) {
- yield take(types.METEOR.SUCCESS);
- }
- const sub = yield RocketChat.subscribe('meteor.loginServiceConfiguration');
- yield take(types.LOGIN.CLOSE);
- if (sub) {
- yield sub.unsubscribe().catch(err => console.warn(err));
- }
- } catch (e) {
- log('watchLoginOpen', e);
- }
-};
-
const handleSetUser = function* handleSetUser() {
yield delay(2000);
const [server, user] = yield all([select(getServer), select(getUser)]);
@@ -155,19 +116,13 @@ const handleSetUser = function* handleSetUser() {
};
const root = function* root() {
- // yield takeLatest(types.METEOR.SUCCESS, handleLoginWhenServerChanges);
- // yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
yield takeLatest(types.LOGIN.SUCCESS, handleLoginSuccess);
- // yield takeLatest(types.LOGIN.SUBMIT, handleLoginSubmit);
yield takeLatest(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
yield takeLatest(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
- yield takeLatest(types.LOGIN.REGISTER_SUCCESS, handleRegisterSuccess);
- yield takeLatest(types.LOGIN.REGISTER_INCOMPLETE, handleRegisterIncomplete);
yield takeLatest(types.LOGIN.SET_USERNAME_SUBMIT, handleSetUsernameSubmit);
yield takeLatest(types.LOGIN.SET_USERNAME_REQUEST, handleSetUsernameRequest);
yield takeLatest(types.LOGOUT, handleLogout);
yield takeLatest(types.FORGOT_PASSWORD.REQUEST, handleForgotPasswordRequest);
- yield takeLatest(types.LOGIN.OPEN, watchLoginOpen);
yield takeLatest(types.USER.SET, handleSetUser);
};
export default root;
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index dadc11dd..9a3d50b4 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -2,6 +2,7 @@ import { put, call, takeLatest } from 'redux-saga/effects';
import { AsyncStorage } from 'react-native';
import { Navigation } from 'react-native-navigation';
import { Provider } from 'react-redux';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { SERVER } from '../actions/actionsTypes';
import * as actions from '../actions';
@@ -14,10 +15,7 @@ import log from '../utils/log';
import store from '../lib/createStore';
let LoginSignupView = null;
-
-const validate = function* validate(server) {
- return yield RocketChat.testServer(server);
-};
+let LoginView = null;
const handleSelectServer = function* handleSelectServer({ server }) {
try {
@@ -29,7 +27,7 @@ const handleSelectServer = function* handleSelectServer({ server }) {
}
const settings = database.objects('settings');
- yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
+ yield put(actions.setAllSettings(RocketChat.parseSettings(RocketChat._filterSettings(settings.slice(0, settings.length)))));
const emojis = database.objects('customEmojis');
yield put(actions.setCustomEmojis(RocketChat.parseEmojis(emojis.slice(0, emojis.length))));
const roles = database.objects('roles');
@@ -47,24 +45,29 @@ const handleSelectServer = function* handleSelectServer({ server }) {
const handleServerRequest = function* handleServerRequest({ server }) {
try {
- if (LoginSignupView == null) {
- LoginSignupView = require('../views/LoginSignupView').default;
- Navigation.registerComponentWithRedux('LoginSignupView', () => LoginSignupView, Provider, store);
- }
-
- yield call(validate, server);
- yield Navigation.push('NewServerView', {
- component: {
- name: 'LoginSignupView',
- options: {
- topBar: {
- title: {
- text: server
- }
- }
- }
+ yield RocketChat.testServer(server);
+ const loginServicesLength = yield RocketChat.getLoginServices(server);
+ if (loginServicesLength === 0) {
+ if (LoginView == null) {
+ LoginView = require('../views/LoginView').default;
+ Navigation.registerComponentWithRedux('LoginView', () => gestureHandlerRootHOC(LoginView), Provider, store);
}
- });
+ yield Navigation.push('NewServerView', {
+ component: {
+ name: 'LoginView'
+ }
+ });
+ } else {
+ if (LoginSignupView == null) {
+ LoginSignupView = require('../views/LoginSignupView').default;
+ Navigation.registerComponentWithRedux('LoginSignupView', () => gestureHandlerRootHOC(LoginSignupView), Provider, store);
+ }
+ yield Navigation.push('NewServerView', {
+ component: {
+ name: 'LoginSignupView'
+ }
+ });
+ }
database.databases.serversDB.write(() => {
database.databases.serversDB.create('servers', { id: server }, true);
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index be205630..193c1327 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -17,6 +17,7 @@ import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n';
import UserItem from '../presentation/UserItem';
import { showErrorAlert } from '../utils/info';
+import { DEFAULT_HEADER } from '../constants/headerOptions';
const styles = StyleSheet.create({
container: {
@@ -85,6 +86,19 @@ const styles = StyleSheet.create({
}))
/** @extends React.Component */
export default class CreateChannelView extends LoggedView {
+ static options() {
+ return {
+ ...DEFAULT_HEADER,
+ topBar: {
+ ...DEFAULT_HEADER.topBar,
+ title: {
+ ...DEFAULT_HEADER.topBar.title,
+ text: I18n.t('Create_Channel')
+ }
+ }
+ };
+ }
+
static propTypes = {
componentId: PropTypes.string,
baseUrl: PropTypes.string,
@@ -109,7 +123,7 @@ export default class CreateChannelView extends LoggedView {
}
componentDidMount() {
- setTimeout(() => {
+ this.timeout = setTimeout(() => {
this.channelNameRef.focus();
}, 600);
}
@@ -140,6 +154,12 @@ export default class CreateChannelView extends LoggedView {
}
}
+ componentWillUnmount() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
onChangeText = (channelName) => {
const { componentId } = this.props;
const rightButtons = [];
diff --git a/app/views/ForgotPasswordView.js b/app/views/ForgotPasswordView.js
index 1d1fc4e5..fc5e0178 100644
--- a/app/views/ForgotPasswordView.js
+++ b/app/views/ForgotPasswordView.js
@@ -1,32 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Text, View, ScrollView } from 'react-native';
+import { Text, ScrollView } from 'react-native';
import { connect } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
import LoggedView from './View';
-import { forgotPasswordInit as forgotPasswordInitAction, forgotPasswordRequest as forgotPasswordRequestAction } from '../actions/login';
+import { forgotPasswordRequest as forgotPasswordRequestAction } from '../actions/login';
import KeyboardView from '../presentation/KeyboardView';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
-import Loading from '../containers/Loading';
-import styles from './Styles';
+import sharedStyles from './Styles';
import { showErrorAlert } from '../utils/info';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n';
+import { DARK_HEADER } from '../constants/headerOptions';
@connect(state => ({
login: state.login
}), dispatch => ({
- forgotPasswordInit: () => dispatch(forgotPasswordInitAction()),
forgotPasswordRequest: email => dispatch(forgotPasswordRequestAction(email))
}))
/** @extends React.Component */
export default class ForgotPasswordView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER
+ };
+ }
+
static propTypes = {
componentId: PropTypes.string,
- forgotPasswordInit: PropTypes.func.isRequired,
forgotPasswordRequest: PropTypes.func.isRequired,
login: PropTypes.object
}
@@ -36,13 +40,14 @@ export default class ForgotPasswordView extends LoggedView {
this.state = {
email: '',
- invalidEmail: false
+ invalidEmail: true
};
}
componentDidMount() {
- const { forgotPasswordInit } = this.props;
- forgotPasswordInit();
+ this.timeout = setTimeout(() => {
+ this.emailInput.focus();
+ }, 600);
}
componentDidUpdate() {
@@ -55,6 +60,12 @@ export default class ForgotPasswordView extends LoggedView {
}
}
+ componentWillUnmount() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
validate = (email) => {
/* eslint-disable no-useless-escape */
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
@@ -80,35 +91,31 @@ export default class ForgotPasswordView extends LoggedView {
return (
-
-
-
- this.validate(email)}
- onSubmitEditing={() => this.resetPassword()}
- testID='forgot-password-view-email'
- />
-
-
-
-
-
- {login.failure ? {login.error.reason} : null}
-
-
+
+
+ {I18n.t('Forgot_password')}
+ { this.emailInput = e; }}
+ placeholder={I18n.t('Email')}
+ keyboardType='email-address'
+ iconLeft='mail'
+ returnKeyType='send'
+ onChangeText={email => this.validate(email)}
+ onSubmitEditing={this.resetPassword}
+ testID='forgot-password-view-email'
+ containerStyle={sharedStyles.inputLastChild}
+ />
+
diff --git a/app/views/LegalView.js b/app/views/LegalView.js
new file mode 100644
index 00000000..da6c889f
--- /dev/null
+++ b/app/views/LegalView.js
@@ -0,0 +1,135 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Text, ScrollView, View, StyleSheet, Image, Platform, Dimensions
+} from 'react-native';
+import { Provider } from 'react-redux';
+import { Navigation } from 'react-native-navigation';
+import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC, RectButton } from 'react-native-gesture-handler';
+
+import sharedStyles from './Styles';
+import scrollPersistTaps from '../utils/scrollPersistTaps';
+import LoggedView from './View';
+import I18n from '../i18n';
+import store from '../lib/createStore';
+import { DARK_HEADER } from '../constants/headerOptions';
+
+let TermsServiceView = null;
+let PrivacyPolicyView = null;
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: '#f7f8fa',
+ flex: 1
+ },
+ scroll: {
+ marginTop: 35,
+ backgroundColor: '#fff',
+ borderColor: '#cbced1',
+ borderTopWidth: StyleSheet.hairlineWidth,
+ borderBottomWidth: StyleSheet.hairlineWidth
+ },
+ separator: {
+ backgroundColor: '#cbced1',
+ height: StyleSheet.hairlineWidth,
+ width: '100%',
+ marginLeft: 20
+ },
+ item: {
+ width: '100%',
+ height: 48,
+ backgroundColor: '#fff',
+ paddingLeft: 20,
+ paddingRight: 10,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between'
+ },
+ text: {
+ ...sharedStyles.textMedium,
+ color: '#0c0d0f',
+ fontSize: 18
+ },
+ disclosureIndicator: {
+ width: 20,
+ height: 20
+ }
+});
+
+const Separator = () => ;
+
+/** @extends React.Component */
+export default class LegalView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER,
+ topBar: {
+ ...DARK_HEADER.topBar,
+ title: {
+ ...DARK_HEADER.topBar.title,
+ text: I18n.t('Legal')
+ },
+ leftButtons: [{
+ id: 'close',
+ icon: Platform.OS === 'android' ? { uri: 'back', scale: Dimensions.get('window').scale } : undefined,
+ text: Platform.OS === 'ios' ? I18n.t('Close') : undefined,
+ testID: 'legal-view-close'
+ }]
+ }
+ };
+ }
+
+ static propTypes = {
+ componentId: PropTypes.string
+ }
+
+ constructor(props) {
+ super('LegalView', props);
+ Navigation.events().bindComponent(this);
+ }
+
+ navigationButtonPressed = ({ buttonId }) => {
+ if (buttonId === 'close') {
+ const { componentId } = this.props;
+ Navigation.dismissModal(componentId);
+ }
+ }
+
+ onPressItem = ({ route }) => {
+ if (route === 'TermsServiceView' && TermsServiceView == null) {
+ TermsServiceView = require('./TermsServiceView').default;
+ Navigation.registerComponentWithRedux('TermsServiceView', () => gestureHandlerRootHOC(TermsServiceView), Provider, store);
+ }
+ if (route === 'PrivacyPolicyView' && PrivacyPolicyView == null) {
+ PrivacyPolicyView = require('./PrivacyPolicyView').default;
+ Navigation.registerComponentWithRedux('PrivacyPolicyView', () => gestureHandlerRootHOC(PrivacyPolicyView), Provider, store);
+ }
+
+ const { componentId } = this.props;
+ Navigation.push(componentId, {
+ component: {
+ name: route
+ }
+ });
+ }
+
+ renderItem = ({ text, route, testID }) => (
+ this.onPressItem({ route })} testID={testID}>
+ {I18n.t(text)}
+
+
+ )
+
+ render() {
+ return (
+
+
+ {this.renderItem({ text: 'Terms_of_Service', route: 'TermsServiceView', testID: 'legal-terms-button' })}
+
+ {this.renderItem({ text: 'Privacy_Policy', route: 'PrivacyPolicyView', testID: 'legal-privacy-button' })}
+
+
+ );
+ }
+}
diff --git a/app/views/LoginSignupView.js b/app/views/LoginSignupView.js
index 626503d2..92f1dfc1 100644
--- a/app/views/LoginSignupView.js
+++ b/app/views/LoginSignupView.js
@@ -1,80 +1,123 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- Text, View, ScrollView, TouchableOpacity, LayoutAnimation, Image, StyleSheet
+ Text, View, ScrollView, Image, StyleSheet, Dimensions, Animated, Easing
} from 'react-native';
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
-import Icon from 'react-native-vector-icons/FontAwesome';
-import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { Base64 } from 'js-base64';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC, RectButton, BorderlessButton } from 'react-native-gesture-handler';
-import { open as openAction, close as closeAction } from '../actions/login';
import LoggedView from './View';
import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import random from '../utils/random';
import Button from '../containers/Button';
-import Loading from '../containers/Loading';
import I18n from '../i18n';
import store from '../lib/createStore';
+import { DARK_HEADER } from '../constants/headerOptions';
const styles = StyleSheet.create({
container: {
- alignItems: 'center',
- justifyContent: 'center'
+ paddingVertical: 30
},
- header: {
- fontSize: 20
+ safeArea: {
+ paddingBottom: 30
},
- servicesContainer: {
- backgroundColor: '#F7F8FA',
- width: '100%',
+ serviceButton: {
borderRadius: 2,
- padding: 16,
- paddingTop: 20,
- marginBottom: 40
+ marginBottom: 10
},
- servicesTitle: {
- color: '#292E35',
- textAlign: 'left',
- fontWeight: '700'
+ serviceButtonContainer: {
+ borderRadius: 2,
+ borderWidth: 1,
+ borderColor: '#e1e5e8',
+ width: '100%',
+ height: 48,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ paddingHorizontal: 15
},
- planetImage: {
- width: 210,
- height: 171,
- marginVertical: 20
+ serviceIcon: {
+ position: 'absolute',
+ left: 15,
+ top: 12,
+ width: 24,
+ height: 24
+ },
+ serviceText: {
+ ...sharedStyles.textRegular,
+ fontSize: 16,
+ color: '#2f343d'
+ },
+ serviceName: {
+ ...sharedStyles.textBold
+ },
+ servicesTogglerContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 5,
+ marginBottom: 30
+ },
+ servicesToggler: {
+ width: 32,
+ height: 31
+ },
+ separatorContainer: {
+ marginTop: 5,
+ marginBottom: 15
+ },
+ separatorLine: {
+ flex: 1,
+ height: 1,
+ backgroundColor: '#e1e5e8'
+ },
+ separatorLineLeft: {
+ marginRight: 15
+ },
+ separatorLineRight: {
+ marginLeft: 15
+ },
+ inverted: {
+ transform: [{ scaleY: -1 }]
}
});
let OAuthView = null;
let LoginView = null;
let RegisterView = null;
+let LegalView = null;
+const SERVICE_HEIGHT = 58;
+const SERVICES_COLLAPSED_HEIGHT = 174;
@connect(state => ({
server: state.server.server,
isFetching: state.login.isFetching,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
- Accounts_OAuth_Facebook: state.settings.Accounts_OAuth_Facebook,
- Accounts_OAuth_Github: state.settings.Accounts_OAuth_Github,
- Accounts_OAuth_Gitlab: state.settings.Accounts_OAuth_Gitlab,
- Accounts_OAuth_Google: state.settings.Accounts_OAuth_Google,
- Accounts_OAuth_Linkedin: state.settings.Accounts_OAuth_Linkedin,
- Accounts_OAuth_Meteor: state.settings.Accounts_OAuth_Meteor,
- Accounts_OAuth_Twitter: state.settings.Accounts_OAuth_Twitter,
+ Site_Name: state.settings.Site_Name,
services: state.login.services
-}), dispatch => ({
- open: () => dispatch(openAction()),
- close: () => dispatch(closeAction())
}))
/** @extends React.Component */
export default class LoginSignupView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER,
+ topBar: {
+ ...DARK_HEADER.topBar,
+ rightButtons: [{
+ id: 'more',
+ icon: { uri: 'more', scale: Dimensions.get('window').scale },
+ testID: 'welcome-view-more'
+ }]
+ }
+ };
+ }
+
static propTypes = {
componentId: PropTypes.string,
- open: PropTypes.func.isRequired,
- close: PropTypes.func.isRequired,
isFetching: PropTypes.bool,
server: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.bool,
@@ -91,33 +134,59 @@ export default class LoginSignupView extends LoggedView {
constructor(props) {
super('LoginSignupView', props);
+ this.state = {
+ collapsed: true,
+ servicesHeight: new Animated.Value(SERVICES_COLLAPSED_HEIGHT)
+ };
+ Navigation.events().bindComponent(this);
+ const { componentId, Site_Name } = this.props;
+ this.setTitle(componentId, Site_Name);
}
- componentDidMount() {
- const { open } = this.props;
- open();
- }
-
- componentWillReceiveProps(nextProps) {
- const { services } = this.props;
- if (services !== nextProps.services) {
- LayoutAnimation.easeInEaseOut();
+ componentDidUpdate(prevProps) {
+ const { componentId, Site_Name } = this.props;
+ if (Site_Name && prevProps.Site_Name !== Site_Name) {
+ this.setTitle(componentId, Site_Name);
}
}
- componentWillUnmount() {
- const { close } = this.props;
- close();
+ setTitle = (componentId, title) => {
+ Navigation.mergeOptions(componentId, {
+ topBar: {
+ title: {
+ text: title
+ }
+ }
+ });
+ }
+
+ navigationButtonPressed = ({ buttonId }) => {
+ if (buttonId === 'more') {
+ if (LegalView == null) {
+ LegalView = require('./LegalView').default;
+ Navigation.registerComponentWithRedux('LegalView', () => gestureHandlerRootHOC(LegalView), Provider, store);
+ }
+
+ Navigation.showModal({
+ stack: {
+ children: [{
+ component: {
+ name: 'LegalView'
+ }
+ }]
+ }
+ });
+ }
}
onPressFacebook = () => {
const { services, server } = this.props;
- const { appId } = services.facebook;
+ const { clientId } = services.facebook;
const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth';
const redirect_uri = `${ server }/_oauth/facebook?close`;
const scope = 'email';
const state = this.getOAuthState();
- const params = `?client_id=${ appId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&display=touch`;
+ const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&display=touch`;
this.openOAuth(`${ endpoint }${ params }`);
}
@@ -190,7 +259,7 @@ export default class LoginSignupView extends LoggedView {
openOAuth = (oAuthUrl) => {
if (OAuthView == null) {
OAuthView = require('./OAuthView').default;
- Navigation.registerComponentWithRedux('OAuthView', () => OAuthView, Provider, store);
+ Navigation.registerComponentWithRedux('OAuthView', () => gestureHandlerRootHOC(OAuthView), Provider, store);
}
Navigation.showModal({
@@ -217,17 +286,17 @@ export default class LoginSignupView extends LoggedView {
login = () => {
if (LoginView == null) {
LoginView = require('./LoginView').default;
- Navigation.registerComponentWithRedux('LoginView', () => LoginView, Provider, store);
+ Navigation.registerComponentWithRedux('LoginView', () => gestureHandlerRootHOC(LoginView), Provider, store);
}
- const { componentId, server } = this.props;
+ const { componentId, Site_Name } = this.props;
Navigation.push(componentId, {
component: {
name: 'LoginView',
options: {
topBar: {
title: {
- text: server
+ text: Site_Name
}
}
}
@@ -238,17 +307,17 @@ export default class LoginSignupView extends LoggedView {
register = () => {
if (RegisterView == null) {
RegisterView = require('./RegisterView').default;
- Navigation.registerComponentWithRedux('RegisterView', () => RegisterView, Provider, store);
+ Navigation.registerComponentWithRedux('RegisterView', () => gestureHandlerRootHOC(RegisterView), Provider, store);
}
- const { componentId, server } = this.props;
+ const { componentId, Site_Name } = this.props;
Navigation.push(componentId, {
component: {
name: 'RegisterView',
options: {
topBar: {
title: {
- text: server
+ text: Site_Name
}
}
}
@@ -256,131 +325,138 @@ export default class LoginSignupView extends LoggedView {
});
}
- renderServices = () => {
- const {
- services, Accounts_OAuth_Facebook, Accounts_OAuth_Github, Accounts_OAuth_Gitlab, Accounts_OAuth_Google, Accounts_OAuth_Linkedin, Accounts_OAuth_Meteor, Accounts_OAuth_Twitter
- } = this.props;
-
- if (!Object.keys(services).length) {
- return null;
+ transitionServicesTo = (height) => {
+ const { servicesHeight } = this.state;
+ if (this._animation) {
+ this._animation.stop();
}
+ this._animation = Animated.timing(servicesHeight, {
+ toValue: height,
+ duration: 300,
+ easing: Easing.easeOutCubic
+ }).start();
+ }
- return (
-
-
- {I18n.t('Or_continue_using_social_accounts')}
-
-
- {Accounts_OAuth_Facebook && services.facebook
- ? (
-
-
-
- )
- : null
- }
- {Accounts_OAuth_Github && services.github
- ? (
-
-
-
- )
- : null
- }
- {Accounts_OAuth_Gitlab && services.gitlab
- ? (
-
-
-
- )
- : null
- }
- {Accounts_OAuth_Google && services.google
- ? (
-
-
-
- )
- : null
- }
- {Accounts_OAuth_Linkedin && services.linkedin
- ? (
-
-
-
- )
- : null
- }
- {Accounts_OAuth_Meteor && services['meteor-developer']
- ? (
-
-
-
- )
- : null
- }
- {Accounts_OAuth_Twitter && services.twitter
- ? (
-
-
-
- )
- : null
- }
+ toggleServices = () => {
+ const { collapsed } = this.state;
+ const { services } = this.props;
+ const { length } = Object.values(services);
+ if (collapsed) {
+ this.transitionServicesTo(SERVICE_HEIGHT * length);
+ } else {
+ this.transitionServicesTo(SERVICES_COLLAPSED_HEIGHT);
+ }
+ this.setState(prevState => ({ collapsed: !prevState.collapsed }));
+ }
+
+ renderServicesSeparator = () => {
+ const { collapsed } = this.state;
+ const { services } = this.props;
+ const { length } = Object.values(services);
+
+ if (length > 3) {
+ return (
+
+
+
+
+
+
+ );
+ }
+ return (
+
+
+
+ );
+ }
+
+ renderItem = (service) => {
+ let { name } = service;
+ name = name === 'meteor-developer' ? 'meteor' : name;
+ const icon = `icon_${ name }`;
+ name = name.charAt(0).toUpperCase() + name.slice(1);
+ let onPress = () => {};
+ switch (service.name) {
+ case 'facebook':
+ onPress = this.onPressFacebook;
+ break;
+ case 'github':
+ onPress = this.onPressGithub;
+ break;
+ case 'gitlab':
+ onPress = this.onPressGitlab;
+ break;
+ case 'google':
+ onPress = this.onPressGoogle;
+ break;
+ case 'linkedin':
+ onPress = this.onPressLinkedin;
+ break;
+ case 'meteor-developer':
+ onPress = this.onPressMeteor;
+ break;
+ case 'twitter':
+ onPress = this.onPressTwitter;
+ break;
+ default:
+ break;
+ }
+ return (
+
+
+
+
+ {I18n.t('Continue_with')} {name}
+
+
+
+ );
+ }
+
+ renderServices = () => {
+ const { servicesHeight } = this.state;
+ const { services } = this.props;
+ const { length } = Object.values(services);
+ const style = {
+ overflow: 'hidden',
+ height: servicesHeight
+ };
+
+
+ if (length > 3) {
+ return (
+
+ {Object.values(services).map(service => this.renderItem(service))}
+
+ );
+ }
+ return (
+
+ {Object.values(services).map(service => this.renderItem(service))}
);
}
render() {
- const { isFetching } = this.props;
-
return (
-
-
-
- {I18n.t('Welcome_title_pt_1')}
- {I18n.t('Welcome_title_pt_2')}
-
-
-
+
+
+ {this.renderServices()}
+ {this.renderServicesSeparator()}
+ {I18n.t('Login_with')} {I18n.t('email')}}
+ type='primary'
+ onPress={() => this.login()}
+ testID='welcome-view-login'
+ />
+ this.register()}
+ testID='welcome-view-register'
+ />
);
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index cf8df7af..013e9571 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -1,93 +1,201 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- Keyboard, Text, ScrollView, View
+ Keyboard, Text, ScrollView, View, StyleSheet, Alert, LayoutAnimation, Dimensions
} from 'react-native';
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import { Answers } from 'react-native-fabric';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import RocketChat from '../lib/rocketchat';
import KeyboardView from '../presentation/KeyboardView';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
-import Loading from '../containers/Loading';
-import styles from './Styles';
+import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
-import { showToast } from '../utils/info';
-import { COLOR_BUTTON_PRIMARY } from '../constants/colors';
import LoggedView from './View';
import I18n from '../i18n';
import store from '../lib/createStore';
+import { DARK_HEADER } from '../constants/headerOptions';
let RegisterView = null;
let ForgotPasswordView = null;
+let LegalView = null;
+
+const styles = StyleSheet.create({
+ buttonsContainer: {
+ flexDirection: 'column',
+ marginTop: 5
+ },
+ bottomContainer: {
+ flexDirection: 'column',
+ alignItems: 'center',
+ marginTop: 10
+ },
+ dontHaveAccount: {
+ ...sharedStyles.textRegular,
+ color: '#9ea2a8',
+ fontSize: 13
+ },
+ createAccount: {
+ ...sharedStyles.textSemibold,
+ color: '#1d74f5',
+ fontSize: 13
+ },
+ loginTitle: {
+ marginVertical: 0,
+ marginTop: 15
+ }
+});
@connect(state => ({
- server: state.server.server,
- failure: state.login.failure,
isFetching: state.login.isFetching,
- reason: state.login.error && state.login.error.reason,
- error: state.login.error && state.login.error.error
+ Site_Name: state.settings.Site_Name,
+ Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
+ Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder
}), () => ({
loginSubmit: params => RocketChat.loginWithPassword(params)
}))
/** @extends React.Component */
export default class LoginView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER,
+ topBar: {
+ ...DARK_HEADER.topBar,
+ rightButtons: [{
+ id: 'more',
+ icon: { uri: 'more', scale: Dimensions.get('window').scale },
+ testID: 'login-view-more'
+ }]
+ }
+ };
+ }
+
static propTypes = {
componentId: PropTypes.string,
loginSubmit: PropTypes.func.isRequired,
login: PropTypes.object,
- server: PropTypes.string,
- error: PropTypes.string,
+ Site_Name: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
- failure: PropTypes.bool,
- isFetching: PropTypes.bool,
- reason: PropTypes.string
+ isFetching: PropTypes.bool
}
constructor(props) {
super('LoginView', props);
this.state = {
username: '',
- password: ''
+ password: '',
+ code: '',
+ showTOTP: false
};
+ Navigation.events().bindComponent(this);
+ const { componentId, Site_Name } = this.props;
+ this.setTitle(componentId, Site_Name);
+ }
+
+ componentDidMount() {
+ this.timeout = setTimeout(() => {
+ this.usernameInput.focus();
+ }, 600);
+ }
+
+ componentDidUpdate(prevProps) {
+ const { componentId, Site_Name } = this.props;
+ if (Site_Name && prevProps.Site_Name !== Site_Name) {
+ this.setTitle(componentId, Site_Name);
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ setTitle = (componentId, title) => {
+ Navigation.mergeOptions(componentId, {
+ topBar: {
+ title: {
+ text: title
+ }
+ }
+ });
+ }
+
+ navigationButtonPressed = ({ buttonId }) => {
+ if (buttonId === 'more') {
+ if (LegalView == null) {
+ LegalView = require('./LegalView').default;
+ Navigation.registerComponentWithRedux('LegalView', () => gestureHandlerRootHOC(LegalView), Provider, store);
+ }
+
+ Navigation.showModal({
+ stack: {
+ children: [{
+ component: {
+ name: 'LegalView'
+ }
+ }]
+ }
+ });
+ }
+ }
+
+ valid = () => {
+ const {
+ username, password, code, showTOTP
+ } = this.state;
+ if (showTOTP) {
+ return code.trim();
+ }
+ return username.trim() && password.trim();
}
submit = async() => {
- const { username, password, code } = this.state;
- const { loginSubmit } = this.props;
-
- if (username.trim() === '' || password.trim() === '') {
- showToast(I18n.t('Email_or_password_field_is_empty'));
+ if (!this.valid()) {
return;
}
+
+ const { username, password, code } = this.state;
+ const { loginSubmit } = this.props;
Keyboard.dismiss();
try {
await loginSubmit({ username, password, code });
Answers.logLogin('Email', true);
- } catch (error) {
- console.warn('LoginView submit', error);
+ } catch (e) {
+ if (e && e.error === 'totp-required') {
+ LayoutAnimation.easeInEaseOut();
+ this.setState({ showTOTP: true });
+ setTimeout(() => {
+ if (this.codeInput && this.codeInput.focus) {
+ this.codeInput.focus();
+ }
+ }, 300);
+ return;
+ }
+ Alert.alert(I18n.t('Oops'), I18n.t('Login_error'));
}
}
register = () => {
if (RegisterView == null) {
RegisterView = require('./RegisterView').default;
- Navigation.registerComponentWithRedux('RegisterView', () => RegisterView, Provider, store);
+ Navigation.registerComponentWithRedux('RegisterView', () => gestureHandlerRootHOC(RegisterView), Provider, store);
}
- const { componentId, server } = this.props;
+ const { componentId, Site_Name } = this.props;
Navigation.push(componentId, {
component: {
name: 'RegisterView',
options: {
topBar: {
title: {
- text: server
+ text: Site_Name
}
}
}
@@ -98,17 +206,17 @@ export default class LoginView extends LoggedView {
forgotPassword = () => {
if (ForgotPasswordView == null) {
ForgotPasswordView = require('./ForgotPasswordView').default;
- Navigation.registerComponentWithRedux('ForgotPasswordView', () => ForgotPasswordView, Provider, store);
+ Navigation.registerComponentWithRedux('ForgotPasswordView', () => gestureHandlerRootHOC(ForgotPasswordView), Provider, store);
}
- const { componentId } = this.props;
+ const { componentId, Site_Name } = this.props;
Navigation.push(componentId, {
component: {
name: 'ForgotPasswordView',
options: {
topBar: {
title: {
- text: I18n.t('Forgot_Password')
+ text: Site_Name
}
}
}
@@ -117,89 +225,99 @@ export default class LoginView extends LoggedView {
}
renderTOTP = () => {
- const { error } = this.props;
- if (/totp/ig.test(error)) {
- return (
+ const { isFetching } = this.props;
+ return (
+
+ {I18n.t('Two_Factor_Authentication')}
+ {I18n.t('Whats_your_2fa')}
this.codeInput = ref}
- label={I18n.t('Code')}
- onChangeText={code => this.setState({ code })}
- placeholder={I18n.t('Code')}
+ onChangeText={value => this.setState({ code: value })}
keyboardType='numeric'
- returnKeyType='done'
+ returnKeyType='send'
autoCapitalize='none'
onSubmitEditing={this.submit}
+ testID='login-view-totp'
+ containerStyle={sharedStyles.inputLastChild}
/>
- );
- }
- return null;
+
+
+ );
+ }
+
+ renderUserForm = () => {
+ const {
+ Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, isFetching
+ } = this.props;
+ return (
+
+ {I18n.t('Login')}
+ { this.usernameInput = e; }}
+ placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
+ keyboardType='email-address'
+ returnKeyType='next'
+ iconLeft='mention'
+ onChangeText={value => this.setState({ username: value })}
+ onSubmitEditing={() => { this.passwordInput.focus(); }}
+ testID='login-view-email'
+ />
+ { this.passwordInput = e; }}
+ placeholder={Accounts_PasswordPlaceholder || I18n.t('Password')}
+ returnKeyType='send'
+ iconLeft='key'
+ secureTextEntry
+ onSubmitEditing={this.submit}
+ onChangeText={value => this.setState({ password: value })}
+ testID='login-view-password'
+ containerStyle={sharedStyles.inputLastChild}
+ />
+
+
+
+ {I18n.t('Dont_Have_An_Account')}
+ {I18n.t('Create_account')}
+
+
+
+ );
}
render() {
- const {
- Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, failure, reason, isFetching
- } = this.props;
-
+ const { showTOTP } = this.state;
return (
-
-
- Login
- this.setState({ username })}
- onSubmitEditing={() => { this.password.focus(); }}
- testID='login-view-email'
- />
-
- { this.password = e; }}
- label={I18n.t('Password')}
- placeholder={Accounts_PasswordPlaceholder || I18n.t('Password')}
- returnKeyType='done'
- iconLeft='key-variant'
- secureTextEntry
- onSubmitEditing={this.submit}
- onChangeText={password => this.setState({ password })}
- testID='login-view-password'
- />
-
- {this.renderTOTP()}
-
-
-
- this.register()}
- >{I18n.t('New_in_RocketChat_question_mark')}
- {I18n.t('Sign_Up')}
-
-
- this.forgotPassword()}
- testID='login-view-forgot-password'
- >{I18n.t('Forgot_password')}
-
-
-
- {failure ? {reason} : null}
-
-
+
+ {!showTOTP ? this.renderUserForm() : null}
+ {showTOTP ? this.renderTOTP() : null}
);
diff --git a/app/views/MentionedMessagesView/index.js b/app/views/MentionedMessagesView/index.js
index 999801fc..ef461b23 100644
--- a/app/views/MentionedMessagesView/index.js
+++ b/app/views/MentionedMessagesView/index.js
@@ -10,6 +10,7 @@ import styles from './styles';
import Message from '../../containers/message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
messages: state.mentionedMessages.messages,
@@ -27,8 +28,11 @@ import I18n from '../../i18n';
export default class MentionedMessagesView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Mentions')
}
}
diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js
index 1204d14a..288a1d0d 100644
--- a/app/views/NewMessageView.js
+++ b/app/views/NewMessageView.js
@@ -6,6 +6,7 @@ import {
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import database from '../lib/realm';
import RocketChat from '../lib/rocketchat';
@@ -17,6 +18,7 @@ import I18n from '../i18n';
import Touch from '../utils/touch';
import SearchBox from '../containers/SearchBox';
import store from '../lib/createStore';
+import { DEFAULT_HEADER } from '../constants/headerOptions';
const styles = StyleSheet.create({
safeAreaView: {
@@ -55,7 +57,9 @@ let SelectedUsersView = null;
export default class NewMessageView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
leftButtons: [{
id: 'cancel',
icon: Platform.OS === 'android' ? { uri: 'back', scale: Dimensions.get('window').scale } : undefined,
@@ -124,7 +128,7 @@ export default class NewMessageView extends LoggedView {
createChannel = () => {
if (SelectedUsersView == null) {
SelectedUsersView = require('./SelectedUsersView').default;
- Navigation.registerComponentWithRedux('SelectedUsersView', () => SelectedUsersView, Provider, store);
+ Navigation.registerComponentWithRedux('SelectedUsersView', () => gestureHandlerRootHOC(SelectedUsersView), Provider, store);
}
const { componentId } = this.props;
diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js
index f3c5c5df..f8c0c304 100644
--- a/app/views/NewServerView.js
+++ b/app/views/NewServerView.js
@@ -15,9 +15,10 @@ import Button from '../containers/Button';
import TextInput from '../containers/TextInput';
import LoggedView from './View';
import I18n from '../i18n';
-import { scale, verticalScale, moderateScale } from '../utils/scaling';
+import { verticalScale, moderateScale } from '../utils/scaling';
import KeyboardView from '../presentation/KeyboardView';
import DeviceInfo from '../utils/deviceInfo';
+import { LIGHT_HEADER } from '../constants/headerOptions';
const styles = StyleSheet.create({
image: {
@@ -27,23 +28,25 @@ const styles = StyleSheet.create({
height: 171
},
title: {
- alignSelf: 'center',
- color: '#2F343D',
+ ...sharedStyles.textBold,
fontSize: moderateScale(22),
- fontWeight: 'bold',
- height: verticalScale(28),
- lineHeight: verticalScale(28)
+ letterSpacing: 0,
+ color: '#2F343D',
+ alignSelf: 'center'
},
inputContainer: {
- marginTop: scale(20),
- marginBottom: scale(20)
+ marginTop: 25,
+ marginBottom: 15
},
input: {
- color: '#9EA2A8',
+ ...sharedStyles.textRegular,
fontSize: 17,
+ letterSpacing: 0,
+ color: '#9EA2A8',
paddingTop: 14,
paddingBottom: 14,
- paddingHorizontal: 16
+ paddingLeft: 16,
+ paddingRight: 16
},
backButton: {
position: 'absolute',
@@ -64,6 +67,7 @@ const defaultServer = 'https://open.rocket.chat';
export default class NewServerView extends LoggedView {
static options() {
return {
+ ...LIGHT_HEADER,
topBar: {
visible: false,
drawBehind: true
@@ -93,7 +97,7 @@ export default class NewServerView extends LoggedView {
connectServer(server);
this.setState({ text: server });
} else {
- setTimeout(() => {
+ this.timeout = setTimeout(() => {
this.input.focus();
}, 600);
}
@@ -106,6 +110,12 @@ export default class NewServerView extends LoggedView {
}
}
+ componentWillUnmount() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
onChangeText = (text) => {
this.setState({ text });
}
@@ -177,10 +187,9 @@ export default class NewServerView extends LoggedView {
this.input = e}
containerStyle={styles.inputContainer}
- inputStyle={styles.input}
placeholder={defaultServer}
value={text}
- returnKeyType='done'
+ returnKeyType='send'
onChangeText={this.onChangeText}
testID='new-server-view-input'
onSubmitEditing={this.submit}
diff --git a/app/views/OAuthView.js b/app/views/OAuthView.js
index cc0b08bc..16497f05 100644
--- a/app/views/OAuthView.js
+++ b/app/views/OAuthView.js
@@ -6,6 +6,7 @@ import { Navigation } from 'react-native-navigation';
import RocketChat from '../lib/rocketchat';
import I18n from '../i18n';
+import { DARK_HEADER } from '../constants/headerOptions';
const userAgentAndroid = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1';
const userAgent = Platform.OS === 'ios' ? 'UserAgent' : userAgentAndroid;
@@ -16,7 +17,9 @@ const userAgent = Platform.OS === 'ios' ? 'UserAgent' : userAgentAndroid;
export default class OAuthView extends React.PureComponent {
static options() {
return {
+ ...DARK_HEADER,
topBar: {
+ ...DARK_HEADER.topBar,
leftButtons: [{
id: 'cancel',
icon: Platform.OS === 'android' ? { uri: 'back', scale: Dimensions.get('window').scale } : undefined,
diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js
index f792e836..e74938b8 100644
--- a/app/views/OnboardingView/index.js
+++ b/app/views/OnboardingView/index.js
@@ -7,6 +7,7 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { selectServerRequest, serverInitAdd, serverFinishAdd } from '../../actions/server';
import I18n from '../../i18n';
@@ -17,6 +18,7 @@ import LoggedView from '../View';
import DeviceInfo from '../../utils/deviceInfo';
import store from '../../lib/createStore';
import EventEmitter from '../../utils/events';
+import { LIGHT_HEADER } from '../../constants/headerOptions';
let NewServerView = null;
@@ -32,6 +34,7 @@ let NewServerView = null;
export default class OnboardingView extends LoggedView {
static options() {
return {
+ ...LIGHT_HEADER,
topBar: {
visible: false,
drawBehind: true
@@ -82,7 +85,7 @@ export default class OnboardingView extends LoggedView {
newServer = (server) => {
if (NewServerView == null) {
NewServerView = require('../NewServerView').default;
- Navigation.registerComponentWithRedux('NewServerView', () => NewServerView, Provider, store);
+ Navigation.registerComponentWithRedux('NewServerView', () => gestureHandlerRootHOC(NewServerView), Provider, store);
}
const { componentId } = this.props;
@@ -147,14 +150,14 @@ export default class OnboardingView extends LoggedView {
render() {
return (
-
+
{I18n.t('Welcome_to_RocketChat')}
{I18n.t('Open_Source_Communication')}
}
+ icon={}
onPress={this.connectServer}
testID='connect-server-button'
/>
@@ -162,14 +165,14 @@ export default class OnboardingView extends LoggedView {
type='secondary'
title={I18n.t('Join_the_community')}
subtitle='open.rocket.chat'
- icon={}
+ icon={}
onPress={this.joinCommunity}
testID='join-community-button'
/>
}
+ icon={}
onPress={this.createWorkspace}
testID='create-workspace-button'
/>
diff --git a/app/views/OnboardingView/styles.js b/app/views/OnboardingView/styles.js
index 87d1ab13..12feae7f 100644
--- a/app/views/OnboardingView/styles.js
+++ b/app/views/OnboardingView/styles.js
@@ -1,6 +1,7 @@
import { StyleSheet } from 'react-native';
-import { verticalScale, scale, moderateScale } from '../../utils/scaling';
+import { verticalScale, moderateScale } from '../../utils/scaling';
+import sharedStyles from '../Styles';
const colors = {
backgroundPrimary: '#1D74F5',
@@ -21,29 +22,26 @@ export default StyleSheet.create({
},
onboarding: {
alignSelf: 'center',
- paddingHorizontal: scale(45),
marginTop: verticalScale(30),
marginBottom: verticalScale(35),
- maxHeight: verticalScale(250),
+ maxHeight: verticalScale(150),
resizeMode: 'contain',
width: 309,
height: 250
},
title: {
- alignSelf: 'center',
- color: '#2F343D',
+ ...sharedStyles.textBold,
+ letterSpacing: 0,
fontSize: moderateScale(24),
- height: moderateScale(28),
- lineHeight: moderateScale(28),
- fontWeight: 'bold'
+ color: '#2F343D',
+ alignSelf: 'center',
+ marginBottom: verticalScale(8)
},
subtitle: {
- alignSelf: 'center',
- color: '#54585E',
+ ...sharedStyles.textRegular,
fontSize: moderateScale(16),
- height: moderateScale(20),
- lineHeight: moderateScale(20),
- fontWeight: 'normal'
+ color: '#54585E',
+ alignSelf: 'center'
},
buttonsContainer: {
marginBottom: verticalScale(10),
@@ -64,13 +62,13 @@ export default StyleSheet.create({
justifyContent: 'center'
},
buttonTitle: {
- fontSize: 16,
- fontWeight: '600'
+ ...sharedStyles.textSemibold,
+ fontSize: 17
},
buttonSubtitle: {
+ ...sharedStyles.textRegular,
color: '#9EA2A8',
- fontSize: 14,
- height: 18
+ fontSize: 15
},
buttonIconContainer: {
width: 65,
diff --git a/app/views/PinnedMessagesView/index.js b/app/views/PinnedMessagesView/index.js
index a37482a1..daba4ae3 100644
--- a/app/views/PinnedMessagesView/index.js
+++ b/app/views/PinnedMessagesView/index.js
@@ -12,6 +12,7 @@ import styles from './styles';
import Message from '../../containers/message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
const PIN_INDEX = 0;
const CANCEL_INDEX = 1;
@@ -34,8 +35,11 @@ const options = [I18n.t('Unpin'), I18n.t('Cancel')];
export default class PinnedMessagesView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Pinned')
}
}
diff --git a/app/views/PrivacyPolicyView.js b/app/views/PrivacyPolicyView.js
index fc84812b..a2b07f49 100644
--- a/app/views/PrivacyPolicyView.js
+++ b/app/views/PrivacyPolicyView.js
@@ -6,12 +6,27 @@ import SafeAreaView from 'react-native-safe-area-view';
import styles from './Styles';
import LoggedView from './View';
+import { DARK_HEADER } from '../constants/headerOptions';
+import I18n from '../i18n';
@connect(state => ({
privacyPolicy: state.settings.Layout_Privacy_Policy
}))
/** @extends React.Component */
export default class PrivacyPolicyView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER,
+ topBar: {
+ ...DARK_HEADER.topBar,
+ title: {
+ ...DARK_HEADER.topBar.title,
+ text: I18n.t('Privacy_Policy')
+ }
+ }
+ };
+ }
+
static propTypes = {
privacyPolicy: PropTypes.string
}
@@ -24,7 +39,7 @@ export default class PrivacyPolicyView extends LoggedView {
const { privacyPolicy } = this.props;
return (
-
+
);
diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js
index 9d07dec1..e3ee4edd 100644
--- a/app/views/ProfileView/index.js
+++ b/app/views/ProfileView/index.js
@@ -27,6 +27,7 @@ import Button from '../../containers/Button';
import Avatar from '../../containers/Avatar';
import Touch from '../../utils/touch';
import Drawer from '../../Drawer';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
user: {
@@ -42,13 +43,16 @@ import Drawer from '../../Drawer';
export default class ProfileView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
leftButtons: [{
id: 'settings',
icon: { uri: 'settings', scale: Dimensions.get('window').scale },
testID: 'rooms-list-view-sidebar'
}],
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Profile')
}
},
diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js
index 902fc599..1c623cf0 100644
--- a/app/views/RegisterView.js
+++ b/app/views/RegisterView.js
@@ -1,50 +1,59 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- Keyboard, Text, View, ScrollView
+ Keyboard, Text, ScrollView, Dimensions, Alert
} from 'react-native';
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
+import equal from 'deep-equal';
-import { registerSubmit as registerSubmitAction, setUsernameSubmit as setUsernameSubmitAction } from '../actions/login';
+import { registerSubmit as registerSubmitAction } from '../actions/login';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
-import Loading from '../containers/Loading';
import KeyboardView from '../presentation/KeyboardView';
-import styles from './Styles';
-import { showToast } from '../utils/info';
+import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import LoggedView from './View';
import I18n from '../i18n';
import store from '../lib/createStore';
+import { DARK_HEADER } from '../constants/headerOptions';
let TermsServiceView = null;
let PrivacyPolicyView = null;
+let LegalView = null;
@connect(state => ({
server: state.server.server,
- Accounts_NamePlaceholder: state.settings.Accounts_NamePlaceholder,
- Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
- Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
- Accounts_RepeatPasswordPlaceholder: state.settings.Accounts_RepeatPasswordPlaceholder,
login: state.login
}), dispatch => ({
- registerSubmit: params => dispatch(registerSubmitAction(params)),
- setUsernameSubmit: params => dispatch(setUsernameSubmitAction(params))
+ registerSubmit: params => dispatch(registerSubmitAction(params))
}))
/** @extends React.Component */
export default class RegisterView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER,
+ topBar: {
+ ...DARK_HEADER.topBar,
+ rightButtons: [{
+ id: 'more',
+ icon: { uri: 'more', scale: Dimensions.get('window').scale },
+ testID: 'register-view-more'
+ }]
+ }
+ };
+ }
+
static propTypes = {
componentId: PropTypes.string,
server: PropTypes.string,
registerSubmit: PropTypes.func.isRequired,
- setUsernameSubmit: PropTypes.func,
Accounts_UsernamePlaceholder: PropTypes.string,
Accounts_NamePlaceholder: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
- Accounts_RepeatPasswordPlaceholder: PropTypes.string,
login: PropTypes.object
}
@@ -54,17 +63,66 @@ export default class RegisterView extends LoggedView {
name: '',
email: '',
password: '',
- confirmPassword: '',
username: ''
};
+ Navigation.events().bindComponent(this);
+ }
+
+ componentDidMount() {
+ this.timeout = setTimeout(() => {
+ this.nameInput.focus();
+ }, 600);
+ }
+
+ componentDidUpdate(prevProps) {
+ const { login, componentId, Site_Name } = this.props;
+ if (login && login.failure && login.error && !equal(login.error, prevProps.login.error)) {
+ Alert.alert(I18n.t('Oops'), login.error.reason);
+ } else if (Site_Name && prevProps.Site_Name !== Site_Name) {
+ this.setTitle(componentId, Site_Name);
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ setTitle = (componentId, title) => {
+ Navigation.mergeOptions(componentId, {
+ topBar: {
+ title: {
+ text: title
+ }
+ }
+ });
+ }
+
+ navigationButtonPressed = ({ buttonId }) => {
+ if (buttonId === 'more') {
+ if (LegalView == null) {
+ LegalView = require('./LegalView').default;
+ Navigation.registerComponentWithRedux('LegalView', () => gestureHandlerRootHOC(LegalView), Provider, store);
+ }
+
+ Navigation.showModal({
+ stack: {
+ children: [{
+ component: {
+ name: 'LegalView'
+ }
+ }]
+ }
+ });
+ }
}
valid = () => {
const {
- name, email, password, confirmPassword
+ name, email, password, username
} = this.state;
- return name.trim() && email.trim()
- && password && confirmPassword && password === confirmPassword;
+ return name.trim() && email.trim() && password.trim() && username.trim();
}
invalidEmail = () => {
@@ -73,39 +131,23 @@ export default class RegisterView extends LoggedView {
}
submit = () => {
+ if (!this.valid()) {
+ return;
+ }
const {
- name, email, password, code
+ name, email, password, username
} = this.state;
const { registerSubmit } = this.props;
-
- if (!this.valid()) {
- showToast(I18n.t('Some_field_is_invalid_or_empty'));
- return;
- }
-
registerSubmit({
- name, email, pass: password, code
+ name, email, pass: password, username
});
Keyboard.dismiss();
}
- usernameSubmit = () => {
- const { username } = this.state;
- const { setUsernameSubmit } = this.props;
-
- if (!username) {
- showToast(I18n.t('Username_is_empty'));
- return;
- }
-
- setUsernameSubmit({ username });
- Keyboard.dismiss();
- }
-
termsService = () => {
if (TermsServiceView == null) {
TermsServiceView = require('./TermsServiceView').default;
- Navigation.registerComponentWithRedux('TermsServiceView', () => TermsServiceView, Provider, store);
+ Navigation.registerComponentWithRedux('TermsServiceView', () => gestureHandlerRootHOC(TermsServiceView), Provider, store);
}
const { componentId } = this.props;
@@ -126,7 +168,7 @@ export default class RegisterView extends LoggedView {
privacyPolicy = () => {
if (PrivacyPolicyView == null) {
PrivacyPolicyView = require('./PrivacyPolicyView').default;
- Navigation.registerComponentWithRedux('PrivacyPolicyView', () => PrivacyPolicyView, Provider, store);
+ Navigation.registerComponentWithRedux('PrivacyPolicyView', () => gestureHandlerRootHOC(PrivacyPolicyView), Provider, store);
}
const { componentId } = this.props;
@@ -144,134 +186,62 @@ export default class RegisterView extends LoggedView {
});
}
- _renderRegister() {
- const { password, confirmPassword } = this.state;
- const {
- login, Accounts_NamePlaceholder, Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_RepeatPasswordPlaceholder
- } = this.props;
-
- if (login.token) {
- return null;
- }
- return (
-
- { this.name = e; }}
- label={Accounts_NamePlaceholder || I18n.t('Name')}
- placeholder={Accounts_NamePlaceholder || I18n.t('Name')}
- returnKeyType='next'
- iconLeft='account'
- onChangeText={name => this.setState({ name })}
- onSubmitEditing={() => { this.email.focus(); }}
- testID='register-view-name'
- />
- { this.email = e; }}
- label={Accounts_EmailOrUsernamePlaceholder || I18n.t('Email')}
- placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Email')}
- returnKeyType='next'
- keyboardType='email-address'
- iconLeft='email'
- onChangeText={email => this.setState({ email })}
- onSubmitEditing={() => { this.password.focus(); }}
- error={this.invalidEmail()}
- testID='register-view-email'
- />
- { this.password = e; }}
- label={Accounts_PasswordPlaceholder || I18n.t('Password')}
- placeholder={Accounts_PasswordPlaceholder || I18n.t('Password')}
- returnKeyType='next'
- iconLeft='key-variant'
- secureTextEntry
- onChangeText={value => this.setState({ password: value })}
- onSubmitEditing={() => { this.confirmPassword.focus(); }}
- testID='register-view-password'
- />
- { this.confirmPassword = e; }}
- inputStyle={
- password
- && confirmPassword
- && confirmPassword !== password ? { borderColor: 'red' } : {}
- }
- label={Accounts_RepeatPasswordPlaceholder || I18n.t('Repeat_Password')}
- placeholder={Accounts_RepeatPasswordPlaceholder || I18n.t('Repeat_Password')}
- returnKeyType='done'
- iconLeft='key-variant'
- secureTextEntry
- onChangeText={value => this.setState({ confirmPassword: value })}
- onSubmitEditing={this.submit}
- testID='register-view-repeat-password'
- />
-
-
-
- {I18n.t('By_proceeding_you_are_agreeing')}
- {I18n.t('Terms_of_Service')}
- {I18n.t('and')}
- {I18n.t('Privacy_Policy')}
-
-
-
-
- );
- }
-
- _renderUsername() {
- const { login, Accounts_UsernamePlaceholder } = this.props;
-
- if (!login.token) {
- return null;
- }
- return (
-
- { this.username = e; }}
- label={Accounts_UsernamePlaceholder || I18n.t('Username')}
- placeholder={Accounts_UsernamePlaceholder || I18n.t('Username')}
- returnKeyType='done'
- iconLeft='at'
- onChangeText={username => this.setState({ username })}
- onSubmitEditing={() => { this.usernameSubmit(); }}
- testID='register-view-username'
- />
-
-
-
-
-
- );
- }
-
render() {
const { login } = this.props;
return (
-
-
-
- {I18n.t('Sign_Up')}
- {this._renderRegister()}
- {this._renderUsername()}
- {login.failure
- ? (
-
- {login.error.reason}
-
- )
- : null
- }
-
+
+
+
+ {I18n.t('Sign_Up')}
+ { this.nameInput = e; }}
+ placeholder={I18n.t('Name')}
+ returnKeyType='next'
+ iconLeft='user'
+ onChangeText={name => this.setState({ name })}
+ onSubmitEditing={() => { this.usernameInput.focus(); }}
+ testID='register-view-name'
+ />
+ { this.usernameInput = e; }}
+ placeholder={I18n.t('Username')}
+ returnKeyType='next'
+ iconLeft='mention'
+ onChangeText={username => this.setState({ username })}
+ onSubmitEditing={() => { this.emailInput.focus(); }}
+ testID='register-view-username'
+ />
+ { this.emailInput = e; }}
+ placeholder={I18n.t('Email')}
+ returnKeyType='next'
+ keyboardType='email-address'
+ iconLeft='mail'
+ onChangeText={email => this.setState({ email })}
+ onSubmitEditing={() => { this.passwordInput.focus(); }}
+ error={this.invalidEmail()}
+ testID='register-view-email'
+ />
+ { this.passwordInput = e; }}
+ placeholder={I18n.t('Password')}
+ returnKeyType='send'
+ iconLeft='key'
+ secureTextEntry
+ onChangeText={value => this.setState({ password: value })}
+ onSubmitEditing={this.submit}
+ testID='register-view-password'
+ containerStyle={sharedStyles.inputLastChild}
+ />
+
+
diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js
index 59922253..7a059d54 100644
--- a/app/views/RoomActionsView/index.js
+++ b/app/views/RoomActionsView/index.js
@@ -24,6 +24,7 @@ import RoomTypeIcon from '../../containers/RoomTypeIcon';
import I18n from '../../i18n';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import store from '../../lib/createStore';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
const renderSeparator = () => ;
@@ -40,8 +41,11 @@ const modules = {};
export default class RoomActionsView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Actions')
}
}
diff --git a/app/views/RoomFilesView/index.js b/app/views/RoomFilesView/index.js
index 5845262a..be34d505 100644
--- a/app/views/RoomFilesView/index.js
+++ b/app/views/RoomFilesView/index.js
@@ -10,6 +10,7 @@ import styles from './styles';
import Message from '../../containers/message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
messages: state.roomFiles.messages,
@@ -27,8 +28,11 @@ import I18n from '../../i18n';
export default class RoomFilesView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Files')
}
}
diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js
index 1ee7be20..8c1c9dac 100644
--- a/app/views/RoomInfoEditView/index.js
+++ b/app/views/RoomInfoEditView/index.js
@@ -21,6 +21,7 @@ import SwitchContainer from './SwitchContainer';
import random from '../../utils/random';
import log from '../../utils/log';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
const PERMISSION_SET_READONLY = 'set-readonly';
const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly';
@@ -44,8 +45,11 @@ const PERMISSIONS_ARRAY = [
export default class RoomInfoEditView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Room_Info_Edit')
}
}
diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js
index 47e120c1..66a0fae0 100644
--- a/app/views/RoomInfoView/index.js
+++ b/app/views/RoomInfoView/index.js
@@ -5,6 +5,7 @@ import { connect, Provider } from 'react-redux';
import moment from 'moment';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import LoggedView from '../View';
import Status from '../../containers/status';
@@ -19,6 +20,7 @@ import RoomTypeIcon from '../../containers/RoomTypeIcon';
import I18n from '../../i18n';
import { iconsMap } from '../../Icons';
import store from '../../lib/createStore';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
const PERMISSION_EDIT_ROOM = 'edit-room';
@@ -46,8 +48,11 @@ let RoomInfoEditView = null;
export default class RoomInfoView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Room_Info')
}
}
@@ -109,7 +114,7 @@ export default class RoomInfoView extends LoggedView {
if (buttonId === 'edit') {
if (RoomInfoEditView == null) {
RoomInfoEditView = require('../RoomInfoEditView').default;
- Navigation.registerComponentWithRedux('RoomInfoEditView', () => RoomInfoEditView, Provider, store);
+ Navigation.registerComponentWithRedux('RoomInfoEditView', () => gestureHandlerRootHOC(RoomInfoEditView), Provider, store);
}
Navigation.push(componentId, {
diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js
index abfba555..e9b42173 100644
--- a/app/views/RoomMembersView/index.js
+++ b/app/views/RoomMembersView/index.js
@@ -18,6 +18,7 @@ import { showToast } from '../../utils/info';
import log from '../../utils/log';
import I18n from '../../i18n';
import SearchBox from '../../containers/SearchBox';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
@@ -26,8 +27,11 @@ import SearchBox from '../../containers/SearchBox';
export default class RoomMembersView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Members')
},
rightButtons: [{
diff --git a/app/views/RoomView/Header/index.js b/app/views/RoomView/Header/index.js
index 67af844f..025b1540 100644
--- a/app/views/RoomView/Header/index.js
+++ b/app/views/RoomView/Header/index.js
@@ -12,9 +12,10 @@ import { STATUS_COLORS } from '../../../constants/colors';
const isIOS = () => Platform.OS === 'ios';
const TITLE_SIZE = 18;
-const ICON_SIZE = 16;
+const ICON_SIZE = 20;
const styles = StyleSheet.create({
container: {
+ flex: 1,
justifyContent: 'center',
backgroundColor: isIOS() ? 'transparent' : '#2F343D',
height: 44
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 0ee080d8..4766d8b3 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -4,7 +4,7 @@ import {
Text, View, LayoutAnimation, ActivityIndicator, Platform
} from 'react-native';
import { connect, Provider } from 'react-redux';
-import { RectButton } from 'react-native-gesture-handler';
+import { RectButton, gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
@@ -27,6 +27,7 @@ import debounce from '../../utils/debounce';
import { iconsMap } from '../../Icons';
import store from '../../lib/createStore';
import ConnectionBadge from '../../containers/ConnectionBadge';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
let RoomActionsView = null;
@@ -50,12 +51,13 @@ let RoomActionsView = null;
export default class RoomView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
- animate: true,
+ ...DEFAULT_HEADER.topBar,
title: {
component: {
name: 'RoomHeaderView',
- alignment: 'fill'
+ alignment: 'left'
}
},
rightButtons: [{
@@ -210,7 +212,7 @@ export default class RoomView extends LoggedView {
if (buttonId === 'more') {
if (RoomActionsView == null) {
RoomActionsView = require('../RoomActionsView').default;
- Navigation.registerComponentWithRedux('RoomActionsView', () => RoomActionsView, Provider, store);
+ Navigation.registerComponentWithRedux('RoomActionsView', () => gestureHandlerRootHOC(RoomActionsView), Provider, store);
}
Navigation.push(componentId, {
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 04dda44a..9a5e44e8 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -7,6 +7,7 @@ import { connect, Provider } from 'react-redux';
import { isEqual } from 'lodash';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import SearchBox from '../../containers/SearchBox';
import ConnectionBadge from '../../containers/ConnectionBadge';
@@ -23,6 +24,7 @@ import Touch from '../../utils/touch';
import { toggleSortDropdown as toggleSortDropdownAction, openSearchHeader as openSearchHeaderAction, closeSearchHeader as closeSearchHeaderAction } from '../../actions/rooms';
import store from '../../lib/createStore';
import Drawer from '../../Drawer';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
const ROW_HEIGHT = 70;
const SCROLL_OFFSET = 56;
@@ -73,7 +75,9 @@ let NewMessageView = null;
export default class RoomsListView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
leftButtons,
rightButtons,
title: {
@@ -186,7 +190,7 @@ export default class RoomsListView extends LoggedView {
if (buttonId === 'newMessage') {
if (NewMessageView == null) {
NewMessageView = require('../NewMessageView').default;
- Navigation.registerComponentWithRedux('NewMessageView', () => NewMessageView, Provider, store);
+ Navigation.registerComponentWithRedux('NewMessageView', () => gestureHandlerRootHOC(NewMessageView), Provider, store);
}
Navigation.showModal({
diff --git a/app/views/SearchMessagesView/index.js b/app/views/SearchMessagesView/index.js
index 22b48ee5..ddb4cca0 100644
--- a/app/views/SearchMessagesView/index.js
+++ b/app/views/SearchMessagesView/index.js
@@ -16,6 +16,7 @@ import Message from '../../containers/message';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import log from '../../utils/log';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
user: {
@@ -29,8 +30,11 @@ import I18n from '../../i18n';
export default class SearchMessagesView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Search')
}
}
diff --git a/app/views/SelectedUsersView.js b/app/views/SelectedUsersView.js
index a70f7be9..9320de37 100644
--- a/app/views/SelectedUsersView.js
+++ b/app/views/SelectedUsersView.js
@@ -6,6 +6,7 @@ import {
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
+import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import {
addUser as addUserAction, removeUser as removeUserAction, reset as resetAction, setLoading as setLoadingAction
@@ -21,6 +22,7 @@ import log from '../utils/log';
import SearchBox from '../containers/SearchBox';
import sharedStyles from './Styles';
import store from '../lib/createStore';
+import { DEFAULT_HEADER } from '../constants/headerOptions';
const styles = StyleSheet.create({
safeAreaView: {
@@ -49,6 +51,12 @@ let CreateChannelView = null;
}))
/** @extends React.Component */
export default class SelectedUsersView extends LoggedView {
+ static options() {
+ return {
+ ...DEFAULT_HEADER
+ };
+ }
+
static propTypes = {
componentId: PropTypes.string,
rid: PropTypes.string,
@@ -112,19 +120,12 @@ export default class SelectedUsersView extends LoggedView {
if (CreateChannelView == null) {
CreateChannelView = require('./CreateChannelView').default;
- Navigation.registerComponentWithRedux('CreateChannelView', () => CreateChannelView, Provider, store);
+ Navigation.registerComponentWithRedux('CreateChannelView', () => gestureHandlerRootHOC(CreateChannelView), Provider, store);
}
Navigation.push(componentId, {
component: {
- name: 'CreateChannelView',
- options: {
- topBar: {
- title: {
- text: I18n.t('Create_Channel')
- }
- }
- }
+ name: 'CreateChannelView'
}
});
} else {
diff --git a/app/views/SetUsernameView.js b/app/views/SetUsernameView.js
new file mode 100644
index 00000000..5851fff0
--- /dev/null
+++ b/app/views/SetUsernameView.js
@@ -0,0 +1,127 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Text, ScrollView, Alert, StyleSheet
+} from 'react-native';
+import { connect } from 'react-redux';
+import SafeAreaView from 'react-native-safe-area-view';
+import equal from 'deep-equal';
+import { Navigation } from 'react-native-navigation';
+
+import { setUsernameSubmit as setUsernameSubmitAction } from '../actions/login';
+import TextInput from '../containers/TextInput';
+import Button from '../containers/Button';
+import KeyboardView from '../presentation/KeyboardView';
+import sharedStyles from './Styles';
+import scrollPersistTaps from '../utils/scrollPersistTaps';
+import LoggedView from './View';
+import I18n from '../i18n';
+import { DARK_HEADER } from '../constants/headerOptions';
+import RocketChat from '../lib/rocketchat';
+
+const styles = StyleSheet.create({
+ loginTitle: {
+ marginVertical: 0,
+ marginTop: 15
+ }
+});
+
+@connect(state => ({
+ server: state.server.server,
+ login: state.login
+}), dispatch => ({
+ setUsernameSubmit: params => dispatch(setUsernameSubmitAction(params))
+}))
+/** @extends React.Component */
+export default class SetUsernameView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER
+ };
+ }
+
+ static propTypes = {
+ componentId: PropTypes.string,
+ server: PropTypes.string,
+ setUsernameSubmit: PropTypes.func.isRequired,
+ Accounts_UsernamePlaceholder: PropTypes.string,
+ login: PropTypes.object
+ }
+
+ constructor(props) {
+ super('SetUsernameView', props);
+ this.state = {
+ username: ''
+ };
+ const { componentId, server } = this.props;
+ Navigation.mergeOptions(componentId, {
+ topBar: {
+ title: {
+ text: server
+ }
+ }
+ });
+ }
+
+ async componentDidMount() {
+ this.timeout = setTimeout(() => {
+ this.usernameInput.focus();
+ }, 600);
+ const suggestion = await RocketChat.getUsernameSuggestion();
+ this.setState({ username: suggestion });
+ }
+
+ componentDidUpdate(prevProps) {
+ const { login } = this.props;
+ if (login && login.failure && login.error && !equal(login.error, prevProps.login.error)) {
+ Alert.alert(I18n.t('Oops'), login.error.reason);
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ submit = () => {
+ const { username } = this.state;
+ const { setUsernameSubmit } = this.props;
+ setUsernameSubmit({ username });
+ }
+
+ render() {
+ const { username } = this.state;
+ const { login } = this.props;
+ return (
+
+
+
+ {I18n.t('Username')}
+ {I18n.t('Set_username_subtitle')}
+ this.usernameInput = e}
+ placeholder={I18n.t('Username')}
+ returnKeyType='send'
+ iconLeft='mention'
+ onChangeText={value => this.setState({ username: value })}
+ value={username}
+ onSubmitEditing={this.submit}
+ testID='set-username-view-input'
+ clearButtonMode='while-editing'
+ containerStyle={sharedStyles.inputLastChild}
+ />
+
+
+
+
+ );
+ }
+}
diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js
index 1a1336ec..d6698e31 100644
--- a/app/views/SettingsView/index.js
+++ b/app/views/SettingsView/index.js
@@ -19,6 +19,7 @@ import { showErrorAlert, showToast } from '../../utils/info';
import log from '../../utils/log';
import { setUser as setUserAction } from '../../actions/login';
import Drawer from '../../Drawer';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
userLanguage: state.login.user && state.login.user.language
@@ -29,13 +30,16 @@ import Drawer from '../../Drawer';
export default class SettingsView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
leftButtons: [{
id: 'settings',
icon: { uri: 'settings', scale: Dimensions.get('window').scale },
testID: 'rooms-list-view-sidebar'
}],
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Settings')
}
},
diff --git a/app/views/SnippetedMessagesView/index.js b/app/views/SnippetedMessagesView/index.js
index 55e5eb62..decf4330 100644
--- a/app/views/SnippetedMessagesView/index.js
+++ b/app/views/SnippetedMessagesView/index.js
@@ -10,6 +10,7 @@ import styles from './styles';
import Message from '../../containers/message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
@connect(state => ({
messages: state.snippetedMessages.messages,
@@ -27,8 +28,11 @@ import I18n from '../../i18n';
export default class SnippetedMessagesView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Snippets')
}
}
diff --git a/app/views/StarredMessagesView/index.js b/app/views/StarredMessagesView/index.js
index fc2d0062..c25b20c8 100644
--- a/app/views/StarredMessagesView/index.js
+++ b/app/views/StarredMessagesView/index.js
@@ -12,6 +12,7 @@ import styles from './styles';
import Message from '../../containers/message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
+import { DEFAULT_HEADER } from '../../constants/headerOptions';
const STAR_INDEX = 0;
const CANCEL_INDEX = 1;
@@ -34,8 +35,11 @@ const options = [I18n.t('Unstar'), I18n.t('Cancel')];
export default class StarredMessagesView extends LoggedView {
static options() {
return {
+ ...DEFAULT_HEADER,
topBar: {
+ ...DEFAULT_HEADER.topBar,
title: {
+ ...DEFAULT_HEADER.topBar.title,
text: I18n.t('Starred')
}
}
diff --git a/app/views/Styles.js b/app/views/Styles.js
index ac9efeee..6af9a8b6 100644
--- a/app/views/Styles.js
+++ b/app/views/Styles.js
@@ -1,7 +1,7 @@
import { StyleSheet, Platform } from 'react-native';
import {
- COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_TEXT, COLOR_SEPARATOR
+ COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_SEPARATOR
} from '../constants/colors';
export default StyleSheet.create({
@@ -110,11 +110,6 @@ export default StyleSheet.create({
fontSize: 13,
fontWeight: '700'
},
- loginOAuthButtons: {
- flexDirection: 'row',
- flexWrap: 'wrap',
- justifyContent: 'center'
- },
validText: {
color: 'green'
},
@@ -184,14 +179,17 @@ export default StyleSheet.create({
opacity5: {
opacity: 0.5
},
-
- loginText: {
- fontWeight: '700',
- color: COLOR_TEXT
- },
loginTitle: {
fontSize: 20,
- marginBottom: 20
+ marginVertical: 15,
+ color: '#2f343d',
+ lineHeight: 28
+ },
+ loginSubtitle: {
+ fontSize: 16,
+ color: '#54585e',
+ lineHeight: 20,
+ marginBottom: 15
},
headerButton: {
backgroundColor: 'transparent',
@@ -216,5 +214,64 @@ export default StyleSheet.create({
borderColor: COLOR_SEPARATOR,
borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: StyleSheet.hairlineWidth
+ },
+ textRegular: {
+ backgroundColor: 'transparent',
+ ...Platform.select({
+ ios: {
+ fontFamily: 'System',
+ fontWeight: '400'
+ },
+ android: {
+ includeFontPadding: false,
+ fontFamily: 'sans-serif',
+ fontWeight: 'normal'
+ }
+ })
+ },
+ textMedium: {
+ backgroundColor: 'transparent',
+ ...Platform.select({
+ ios: {
+ fontFamily: 'System',
+ fontWeight: '500'
+ },
+ android: {
+ includeFontPadding: false,
+ fontFamily: 'sans-serif-medium',
+ fontWeight: 'normal'
+ }
+ })
+ },
+ textSemibold: {
+ backgroundColor: 'transparent',
+ ...Platform.select({
+ ios: {
+ fontFamily: 'System',
+ fontWeight: '600'
+ },
+ android: {
+ includeFontPadding: false,
+ fontFamily: 'sans-serif',
+ fontWeight: 'bold'
+ }
+ })
+ },
+ textBold: {
+ backgroundColor: 'transparent',
+ ...Platform.select({
+ ios: {
+ fontFamily: 'System',
+ fontWeight: '700'
+ },
+ android: {
+ includeFontPadding: false,
+ fontFamily: 'sans-serif',
+ fontWeight: 'bold'
+ }
+ })
+ },
+ inputLastChild: {
+ marginBottom: 15
}
});
diff --git a/app/views/TermsServiceView.js b/app/views/TermsServiceView.js
index ce9f9c27..1de3ed8c 100644
--- a/app/views/TermsServiceView.js
+++ b/app/views/TermsServiceView.js
@@ -6,12 +6,27 @@ import SafeAreaView from 'react-native-safe-area-view';
import styles from './Styles';
import LoggedView from './View';
+import { DARK_HEADER } from '../constants/headerOptions';
+import I18n from '../i18n';
@connect(state => ({
termsService: state.settings.Layout_Terms_of_Service
}))
/** @extends React.Component */
export default class TermsServiceView extends LoggedView {
+ static options() {
+ return {
+ ...DARK_HEADER,
+ topBar: {
+ ...DARK_HEADER.topBar,
+ title: {
+ ...DARK_HEADER.topBar.title,
+ text: I18n.t('Terms_of_Service')
+ }
+ }
+ };
+ }
+
static propTypes = {
termsService: PropTypes.string
}
@@ -23,7 +38,7 @@ export default class TermsServiceView extends LoggedView {
render() {
const { termsService } = this.props;
return (
-
+
);
diff --git a/app/views/index.js b/app/views/index.js
index e84fc69f..728caa80 100644
--- a/app/views/index.js
+++ b/app/views/index.js
@@ -12,12 +12,12 @@ import SettingsView from './SettingsView';
import Sidebar from '../containers/Sidebar';
export const registerScreens = (store) => {
- Navigation.registerComponentWithRedux('OnboardingView', () => OnboardingView, Provider, store);
- Navigation.registerComponentWithRedux('ProfileView', () => ProfileView, Provider, store);
- Navigation.registerComponentWithRedux('RoomsListHeaderView', () => RoomsListHeaderView, Provider, store);
+ Navigation.registerComponentWithRedux('OnboardingView', () => gestureHandlerRootHOC(OnboardingView), Provider, store);
+ Navigation.registerComponentWithRedux('ProfileView', () => gestureHandlerRootHOC(ProfileView), Provider, store);
+ Navigation.registerComponentWithRedux('RoomsListHeaderView', () => gestureHandlerRootHOC(RoomsListHeaderView), Provider, store);
Navigation.registerComponentWithRedux('RoomsListView', () => gestureHandlerRootHOC(RoomsListView), Provider, store);
Navigation.registerComponentWithRedux('RoomView', () => gestureHandlerRootHOC(RoomView), Provider, store);
- Navigation.registerComponentWithRedux('RoomHeaderView', () => RoomHeaderView, Provider, store);
- Navigation.registerComponentWithRedux('SettingsView', () => SettingsView, Provider, store);
- Navigation.registerComponentWithRedux('Sidebar', () => Sidebar, Provider, store);
+ Navigation.registerComponentWithRedux('RoomHeaderView', () => gestureHandlerRootHOC(RoomHeaderView), Provider, store);
+ Navigation.registerComponentWithRedux('SettingsView', () => gestureHandlerRootHOC(SettingsView), Provider, store);
+ Navigation.registerComponentWithRedux('Sidebar', () => gestureHandlerRootHOC(Sidebar), Provider, store);
};
diff --git a/e2e/00-onboarding.spec.js b/e2e/00-onboarding.spec.js
index a7592195..377e101b 100644
--- a/e2e/00-onboarding.spec.js
+++ b/e2e/00-onboarding.spec.js
@@ -40,8 +40,8 @@ describe('Onboarding', () => {
await element(by.id('join-community-button')).tap();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('welcome-view'))).toBeVisible();
- await waitFor(element(by.text('https://open.rocket.chat'))).toBeVisible().withTimeout(60000);
- await expect(element(by.text('https://open.rocket.chat'))).toBeVisible();
+ await waitFor(element(by.text('Rocket.Chat'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.text('Rocket.Chat'))).toBeVisible();
});
it('should navigate to new server', async() => {
@@ -60,14 +60,25 @@ describe('Onboarding', () => {
await expect(element(by.text(errorText))).toBeVisible();
});
- it('should enter a valid server and navigate to welcome', async() => {
+ it('should enter a valid server with login services and navigate to welcome', async() => {
await element(by.text('OK')).tap();
- await element(by.id('new-server-view-input')).replaceText(data.server);
+ await element(by.id('new-server-view-input')).replaceText('open');
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('welcome-view'))).toBeVisible();
});
+ it('should enter a valid server without login services and navigate to login', async() => {
+ await device.reloadReactNative();
+ await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('connect-server-button')).tap();
+ await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
+ await element(by.id('new-server-view-input')).replaceText(data.server);
+ await element(by.id('new-server-view-button')).tap();
+ await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('login-view'))).toBeVisible();
+ });
+
afterEach(async() => {
takeScreenshot();
diff --git a/e2e/01-welcome.spec.js b/e2e/01-welcome.spec.js
index 81a6075b..bcb3899a 100644
--- a/e2e/01-welcome.spec.js
+++ b/e2e/01-welcome.spec.js
@@ -5,6 +5,12 @@ const { takeScreenshot } = require('./helpers/screenshot');
const { tapBack } = require('./helpers/app');
describe('Welcome screen', () => {
+ before(async() => {
+ await device.reloadReactNative();
+ await element(by.id('join-community-button')).tap();
+ await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
+ })
+
describe('Render', async() => {
it('should have welcome screen', async() => {
await expect(element(by.id('welcome-view'))).toBeVisible();
@@ -30,14 +36,20 @@ describe('Welcome screen', () => {
await element(by.id('welcome-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible();
- await tapBack();
});
it('should navigate to register', async() => {
+ await tapBack();
await element(by.id('welcome-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('register-view'))).toBeVisible();
+ });
+
+ it('should navigate to legal', async() => {
await tapBack();
+ await element(by.id('welcome-view-more')).tap();
+ await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('legal-view'))).toBeVisible();
});
afterEach(async() => {
diff --git a/e2e/02-legal.spec.js b/e2e/02-legal.spec.js
new file mode 100644
index 00000000..19403a10
--- /dev/null
+++ b/e2e/02-legal.spec.js
@@ -0,0 +1,56 @@
+const {
+ device, expect, element, by, waitFor
+} = require('detox');
+const { takeScreenshot } = require('./helpers/screenshot');
+const { tapBack } = require('./helpers/app');
+
+describe('Legal screen', () => {
+ before(async() => {
+ await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('legal-view'))).toBeVisible();
+ })
+
+ describe('Render', async() => {
+ it('should have legal screen', async() => {
+ await expect(element(by.id('legal-view'))).toBeVisible();
+ });
+
+ it('should have terms of service button', async() => {
+ await expect(element(by.id('legal-terms-button'))).toBeVisible();
+ });
+
+ it('should have privacy policy button', async() => {
+ await expect(element(by.id('legal-privacy-button'))).toBeVisible();
+ });
+
+ after(async() => {
+ takeScreenshot();
+ });
+ });
+
+ describe('Usage', async() => {
+ it('should navigate to terms', async() => {
+ await element(by.id('legal-terms-button')).tap();
+ await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('terms-view'))).toBeVisible();
+ });
+
+ it('should navigate to privacy', async() => {
+ await tapBack();
+ await element(by.id('legal-privacy-button')).tap();
+ await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('privacy-view'))).toBeVisible();
+ });
+
+ it('should navigate to welcome', async() => {
+ await tapBack();
+ await element(by.id('legal-view-close')).tap();
+ await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('welcome-view'))).toBeVisible();
+ });
+
+ afterEach(async() => {
+ takeScreenshot();
+ });
+ });
+});
diff --git a/e2e/03-createuser.spec.js b/e2e/03-createuser.spec.js
deleted file mode 100644
index e4d9615b..00000000
--- a/e2e/03-createuser.spec.js
+++ /dev/null
@@ -1,139 +0,0 @@
-const {
- device, expect, element, by, waitFor
-} = require('detox');
-const { takeScreenshot } = require('./helpers/screenshot');
-const { logout, sleep } = require('./helpers/app');
-const data = require('./data');
-
-describe('Create user screen', () => {
- before(async() => {
- await device.reloadReactNative();
- await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
- await element(by.id('connect-server-button')).tap();
- await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
- await element(by.id('new-server-view-input')).replaceText(data.server);
- await element(by.id('new-server-view-button')).tap();
- await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
- await element(by.id('welcome-view-register')).tap();
- await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
- });
-
- describe('Render', () => {
- it('should have create user screen', async() => {
- await expect(element(by.id('register-view'))).toBeVisible();
- });
-
- it('should have name input', async() => {
- await expect(element(by.id('register-view-name'))).toBeVisible();
- });
-
- it('should have email input', async() => {
- await expect(element(by.id('register-view-email'))).toBeVisible();
- });
-
- it('should have password input', async() => {
- await expect(element(by.id('register-view-password'))).toBeVisible();
- });
-
- it('should have show password icon', async() => {
- await expect(element(by.id('register-view-password-icon-right'))).toBeVisible();
- });
-
- it('should have repeat password input', async() => {
- await expect(element(by.id('register-view-repeat-password'))).toBeVisible();
- });
-
- it('should have repeat password icon', async() => {
- await expect(element(by.id('register-view-repeat-password-icon-right'))).toBeVisible();
- });
-
- it('should have submit button', async() => {
- await expect(element(by.id('register-view-submit'))).toBeVisible();
- });
-
- after(async() => {
- takeScreenshot();
- });
- });
-
- describe('Usage', () => {
- it('should submit empty form and raise error', async() => {
- await element(by.id('register-view-submit')).tap();
- await waitFor(element(by.text('Some field is invalid or empty'))).toBeVisible().withTimeout(10000);
- await expect(element(by.text('Some field is invalid or empty'))).toBeVisible();
- });
-
- it('should submit different passwords and raise error', async() => {
- await element(by.id('register-view-name')).replaceText(data.user);
- await element(by.id('register-view-email')).replaceText(data.email);
- await element(by.id('register-view-password')).replaceText('abc');
- await element(by.id('register-view-repeat-password')).replaceText('xyz');
- await element(by.id('register-view-submit')).tap();
- await waitFor(element(by.text('Some field is invalid or empty'))).toBeVisible().withTimeout(10000);
- await expect(element(by.text('Some field is invalid or empty'))).toBeVisible();
- });
-
- it('should submit invalid email and raise error', async() => {
- await element(by.id('register-view-name')).replaceText(data.user);
- await element(by.id('register-view-email')).replaceText('invalidemail');
- await element(by.id('register-view-password')).replaceText(data.password);
- await element(by.id('register-view-repeat-password')).replaceText(data.password);
- await element(by.id('register-view-submit')).tap();
- await waitFor(element(by.id('register-view-error'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('register-view-error'))).toBeVisible();
- await expect(element(by.id('register-view-error'))).toHaveText('Invalid email invalidemail');
- });
-
- it('should submit email already taken and raise error', async() => {
- await element(by.id('register-view-name')).replaceText(data.user);
- await element(by.id('register-view-email')).replaceText('diego.mello@rocket.chat');
- await element(by.id('register-view-password')).replaceText(data.password);
- await element(by.id('register-view-repeat-password')).replaceText(data.password);
- await element(by.id('register-view-submit')).tap();
- await waitFor(element(by.id('register-view-error'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('register-view-error'))).toBeVisible();
- await expect(element(by.id('register-view-error'))).toHaveText('Email already exists.');
- });
-
- it('should complete first part of register', async() => {
- await element(by.id('register-view-name')).replaceText(data.user);
- await element(by.id('register-view-email')).replaceText(data.email);
- await element(by.id('register-view-password')).replaceText(data.password);
- await element(by.id('register-view-repeat-password')).replaceText(data.password);
- await element(by.id('register-view-submit')).tap();
- await waitFor(element(by.id('register-view-username'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('register-view-username'))).toBeVisible();
- });
-
- it('should submit empty username and raise error', async() => {
- await element(by.id('register-view-submit-username')).tap();
- await waitFor(element(by.text('Username is empty'))).toBeVisible().withTimeout(10000);
- await expect(element(by.text('Username is empty'))).toBeVisible();
- });
-
- it('should submit already taken username and raise error', async() => {
- await element(by.id('register-view-username')).replaceText('diego.mello');
- await element(by.id('register-view-submit-username')).tap();
- await waitFor(element(by.id('register-view-error'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('register-view-error'))).toBeVisible();
- });
-
- it('should finish register', async() => {
- await sleep(2000);
- await element(by.id('register-view-username')).replaceText(data.user);
- await element(by.id('register-view-submit-username')).tap();
- await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
- });
-
- // TODO: terms and privacy
-
- afterEach(async() => {
- takeScreenshot();
- });
-
- after(async() => {
- await logout();
- });
- });
-});
diff --git a/e2e/02-forgotpassword.spec.js b/e2e/03-forgotpassword.spec.js
similarity index 100%
rename from e2e/02-forgotpassword.spec.js
rename to e2e/03-forgotpassword.spec.js
diff --git a/e2e/04-createuser.spec.js b/e2e/04-createuser.spec.js
new file mode 100644
index 00000000..79e33d97
--- /dev/null
+++ b/e2e/04-createuser.spec.js
@@ -0,0 +1,118 @@
+const {
+ device, expect, element, by, waitFor
+} = require('detox');
+const { takeScreenshot } = require('./helpers/screenshot');
+const { logout, sleep } = require('./helpers/app');
+const data = require('./data');
+
+async function navigateToRegister() {
+ await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('connect-server-button')).tap();
+ await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
+ await element(by.id('new-server-view-input')).replaceText(data.server);
+ await element(by.id('new-server-view-button')).tap();
+ // we're assuming the server don't have login services and the navigation will jump to login
+ await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
+ await element(by.id('login-view-register')).tap();
+ await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
+}
+
+describe('Create user screen', () => {
+ before(async() => {
+ await device.reloadReactNative();
+ await navigateToRegister();
+ });
+
+ describe('Render', () => {
+ it('should have create user screen', async() => {
+ await expect(element(by.id('register-view'))).toBeVisible();
+ });
+
+ it('should have name input', async() => {
+ await expect(element(by.id('register-view-name'))).toBeVisible();
+ });
+
+ it('should have email input', async() => {
+ await expect(element(by.id('register-view-email'))).toBeVisible();
+ });
+
+ it('should have password input', async() => {
+ await expect(element(by.id('register-view-password'))).toBeVisible();
+ });
+
+ it('should have show password icon', async() => {
+ await expect(element(by.id('register-view-password-icon-right'))).toBeVisible();
+ });
+
+ it('should have submit button', async() => {
+ await expect(element(by.id('register-view-submit'))).toBeVisible();
+ });
+
+ it('should have legal button', async() => {
+ await expect(element(by.id('register-view-more'))).toBeVisible();
+ });
+
+ after(async() => {
+ takeScreenshot();
+ });
+ });
+
+ describe('Usage', () => {
+ it('should submit invalid email and raise error', async() => {
+ const invalidEmail = 'invalidemail';
+ await element(by.id('register-view-name')).replaceText(data.user);
+ await element(by.id('register-view-username')).replaceText(data.user);
+ await element(by.id('register-view-email')).replaceText(invalidEmail);
+ await element(by.id('register-view-password')).replaceText(data.password);
+ await element(by.id('register-view-submit')).tap();
+ await waitFor(element(by.text(`Invalid email ${ invalidEmail }`)).atIndex(0)).toExist().withTimeout(10000);
+ await expect(element(by.text(`Invalid email ${ invalidEmail }`)).atIndex(0)).toExist();
+ await element(by.text('OK')).tap();
+ });
+
+ it('should submit email already taken and raise error', async() => {
+ const invalidEmail = 'invalidemail';
+ await element(by.id('register-view-name')).replaceText(data.user);
+ await element(by.id('register-view-username')).replaceText(data.user);
+ await element(by.id('register-view-email')).replaceText('diego.mello@rocket.chat');
+ await element(by.id('register-view-password')).replaceText(data.password);
+ await element(by.id('register-view-submit')).tap();
+ await waitFor(element(by.text('Email already exists.')).atIndex(0)).toExist().withTimeout(10000);
+ await expect(element(by.text('Email already exists.')).atIndex(0)).toExist();
+ await element(by.text('OK')).tap();
+ });
+
+ it('should register', async() => {
+ await element(by.id('register-view-name')).replaceText(data.user);
+ await element(by.id('register-view-username')).replaceText(data.user);
+ await element(by.id('register-view-email')).replaceText(data.email);
+ await element(by.id('register-view-password')).replaceText(data.password);
+ await element(by.id('register-view-submit')).tap();
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('rooms-list-view'))).toBeVisible();
+ });
+
+ it('should pick an existing username, suggest another and finish register', async() => {
+ await logout();
+ await navigateToRegister();
+ await element(by.id('register-view-name')).replaceText(data.user);
+ await element(by.id('register-view-username')).replaceText(data.user);
+ await element(by.id('register-view-email')).replaceText(`${ data.email }2`);
+ await element(by.id('register-view-password')).replaceText(data.password);
+ await element(by.id('register-view-submit')).tap();
+ await waitFor(element(by.id('set-username-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('set-username-view'))).toBeVisible();
+ await element(by.id('set-username-view-submit')).tap();
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('rooms-list-view'))).toBeVisible();
+ });
+
+ afterEach(async() => {
+ takeScreenshot();
+ });
+
+ after(async() => {
+ await logout();
+ });
+ });
+});
diff --git a/e2e/04-login.spec.js b/e2e/05-login.spec.js
similarity index 83%
rename from e2e/04-login.spec.js
rename to e2e/05-login.spec.js
index 4540f91e..21bdf843 100644
--- a/e2e/04-login.spec.js
+++ b/e2e/05-login.spec.js
@@ -39,6 +39,10 @@ describe('Login screen', () => {
await expect(element(by.id('login-view-forgot-password'))).toBeVisible();
});
+ it('should have legal button', async() => {
+ await expect(element(by.id('login-view-more'))).toBeVisible();
+ });
+
after(async() => {
takeScreenshot();
});
@@ -58,21 +62,14 @@ describe('Login screen', () => {
await expect(element(by.id('forgot-password-view'))).toBeVisible();
await tapBack();
});
-
- it('should navigate to welcome', async() => {
- await tapBack();
- await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('welcome-view'))).toBeVisible();
- await element(by.id('welcome-view-login')).tap();
- await expect(element(by.id('login-view'))).toBeVisible();
- });
it('should insert wrong password and get error', async() => {
await element(by.id('login-view-email')).replaceText(data.user);
await element(by.id('login-view-password')).replaceText('error');
await element(by.id('login-view-submit')).tap();
- await waitFor(element(by.text('User or Password incorrect'))).toBeVisible().withTimeout(10000);
- await expect(element(by.text('User or Password incorrect'))).toBeVisible();
+ await waitFor(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible().withTimeout(10000);
+ await expect(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible();
+ await element(by.text('OK')).tap();
});
it('should login with success', async() => {
diff --git a/e2e/05-roomslist.spec.js b/e2e/06-roomslist.spec.js
similarity index 100%
rename from e2e/05-roomslist.spec.js
rename to e2e/06-roomslist.spec.js
diff --git a/e2e/06-createroom.spec.js b/e2e/07-createroom.spec.js
similarity index 100%
rename from e2e/06-createroom.spec.js
rename to e2e/07-createroom.spec.js
diff --git a/e2e/07-room.spec.js b/e2e/08-room.spec.js
similarity index 100%
rename from e2e/07-room.spec.js
rename to e2e/08-room.spec.js
diff --git a/e2e/08-roomactions.spec.js b/e2e/09-roomactions.spec.js
similarity index 100%
rename from e2e/08-roomactions.spec.js
rename to e2e/09-roomactions.spec.js
diff --git a/e2e/09-roominfo.spec.js b/e2e/10-roominfo.spec.js
similarity index 100%
rename from e2e/09-roominfo.spec.js
rename to e2e/10-roominfo.spec.js
diff --git a/e2e/10-changeserver.spec.js b/e2e/11-changeserver.spec.js
similarity index 79%
rename from e2e/10-changeserver.spec.js
rename to e2e/11-changeserver.spec.js
index c6408438..3dfaf0e5 100644
--- a/e2e/10-changeserver.spec.js
+++ b/e2e/11-changeserver.spec.js
@@ -22,18 +22,26 @@ describe('Change server', () => {
await element(by.id('new-server-view-input')).replaceText(data.alternateServer);
await element(by.id('new-server-view-button')).tap();
// Navigate to register
- await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
- await element(by.id('welcome-view-register')).tap();
+ // await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
+ // await element(by.id('welcome-view-register')).tap();
+ // await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
+ try {
+ await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('login-view'))).toBeVisible();
+ await element(by.id('login-view-register')).tap();
+ } catch (error) {
+ await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('welcome-view'))).toBeVisible();
+ await element(by.id('welcome-view-register')).tap();
+ }
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('register-view'))).toBeVisible();
// Register new user
await element(by.id('register-view-name')).replaceText(data.user);
+ await element(by.id('register-view-username')).replaceText(data.user);
await element(by.id('register-view-email')).replaceText(data.email);
await element(by.id('register-view-password')).replaceText(data.password);
- await element(by.id('register-view-repeat-password')).replaceText(data.password);
await element(by.id('register-view-submit')).tap();
- await waitFor(element(by.id('register-view-username'))).toBeVisible().withTimeout(60000);
- await element(by.id('register-view-username')).replaceText(data.user);
- await element(by.id('register-view-submit-username')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
// For a sanity test, to make sure roomslist is showing correct rooms
diff --git a/e2e/11-broadcast.spec.js b/e2e/12-broadcast.spec.js
similarity index 94%
rename from e2e/11-broadcast.spec.js
rename to e2e/12-broadcast.spec.js
index 7da696cf..ab314fbf 100644
--- a/e2e/11-broadcast.spec.js
+++ b/e2e/12-broadcast.spec.js
@@ -1,6 +1,8 @@
const {
device, expect, element, by, waitFor
} = require('detox');
+const OTP = require('otp.js');
+const GA = OTP.googleAuthenticator;
const { takeScreenshot } = require('./helpers/screenshot');
const { logout, navigateToLogin, login, tapBack } = require('./helpers/app');
const data = require('./data');
@@ -58,15 +60,19 @@ describe('Broadcast room', () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
await logout();
await navigateToLogin();
+ // 2FA login in stable:detox
await element(by.id('login-view-email')).replaceText(data.alternateUser);
await element(by.id('login-view-password')).replaceText(data.alternateUserPassword);
await element(by.id('login-view-submit')).tap();
+ const code = GA.gen(data.alternateUserTOTPSecret);
+ await element(by.id('login-view-totp')).replaceText(code);
+ await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// await device.reloadReactNative(); // remove after fix logout
// await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-search')).replaceText(`broadcast${ data.random }`);
- await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible();
+ await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000);
+ await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist();
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.text(`broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
diff --git a/e2e/12-profile.spec.js b/e2e/13-profile.spec.js
similarity index 83%
rename from e2e/12-profile.spec.js
rename to e2e/13-profile.spec.js
index c1ded9c6..f1687c56 100644
--- a/e2e/12-profile.spec.js
+++ b/e2e/13-profile.spec.js
@@ -24,15 +24,15 @@ describe('Profile screen', () => {
});
it('should have avatar', async() => {
- await expect(element(by.id('profile-view-avatar')).atIndex(0)).toBeVisible();
+ await expect(element(by.id('profile-view-avatar')).atIndex(0)).toExist();
});
it('should have name', async() => {
- await expect(element(by.id('profile-view-name'))).toBeVisible();
+ await expect(element(by.id('profile-view-name'))).toExist();
});
it('should have username', async() => {
- await expect(element(by.id('profile-view-username'))).toBeVisible();
+ await expect(element(by.id('profile-view-username'))).toExist();
});
it('should have email', async() => {
@@ -40,31 +40,31 @@ describe('Profile screen', () => {
});
it('should have new password', async() => {
- await expect(element(by.id('profile-view-new-password'))).toBeVisible();
+ await expect(element(by.id('profile-view-new-password'))).toExist();
});
it('should have avatar url', async() => {
- await expect(element(by.id('profile-view-avatar-url'))).toBeVisible();
+ await expect(element(by.id('profile-view-avatar-url'))).toExist();
});
it('should have reset avatar button', async() => {
- await waitFor(element(by.id('profile-view-reset-avatar'))).toBeVisible().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-reset-avatar'))).toBeVisible();
+ await waitFor(element(by.id('profile-view-reset-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
+ await expect(element(by.id('profile-view-reset-avatar'))).toExist();
});
it('should have upload avatar button', async() => {
- await waitFor(element(by.id('profile-view-upload-avatar'))).toBeVisible().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-upload-avatar'))).toBeVisible();
+ await waitFor(element(by.id('profile-view-upload-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
+ await expect(element(by.id('profile-view-upload-avatar'))).toExist();
});
it('should have avatar url button', async() => {
- await waitFor(element(by.id('profile-view-avatar-url-button'))).toBeVisible().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-avatar-url-button'))).toBeVisible();
+ await waitFor(element(by.id('profile-view-avatar-url-button'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
+ await expect(element(by.id('profile-view-avatar-url-button'))).toExist();
});
it('should have submit button', async() => {
- await waitFor(element(by.id('profile-view-submit'))).toBeVisible().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-submit'))).toBeVisible();
+ await waitFor(element(by.id('profile-view-submit'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
+ await expect(element(by.id('profile-view-submit'))).toExist();
});
after(async() => {
diff --git a/e2e/data.js b/e2e/data.js
index 8cdd99e0..b78e226f 100644
--- a/e2e/data.js
+++ b/e2e/data.js
@@ -7,6 +7,7 @@ const data = {
password: `password${ value }`,
alternateUser: 'detox',
alternateUserPassword: '123',
+ alternateUserTOTPSecret: 'HJGECLDOH5RCKJSWMREXAKKENVZXKOJ6I5ZTKPSRIEQWGOK5K5KA',
email: `diego.mello+e2e${ value }@rocket.chat`,
random: value
}
diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js
index 6825c730..10108332 100644
--- a/e2e/helpers/app.js
+++ b/e2e/helpers/app.js
@@ -14,9 +14,16 @@ async function addServer() {
async function navigateToLogin() {
await addServer();
- await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
- await element(by.id('welcome-view-login')).tap();
- await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
+ try {
+ await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('login-view'))).toBeVisible();
+ } catch (error) {
+ await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('welcome-view'))).toBeVisible();
+ await element(by.id('welcome-view-login')).tap();
+ await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('login-view'))).toBeVisible();
+ }
}
async function login() {
diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj
index 9b8f0204..2ecdd111 100644
--- a/ios/RocketChatRN.xcodeproj/project.pbxproj
+++ b/ios/RocketChatRN.xcodeproj/project.pbxproj
@@ -64,7 +64,6 @@
AE5D35882AE04CC29630FB3D /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DC6EE17B5550465E98C70FF0 /* Entypo.ttf */; };
B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B88F58461FBF55E200B352B8 /* libRCTPushNotification.a */; };
B8971BB2202A093B0000D245 /* libKeyboardTrackingView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */; };
- B8C682A81FD850F4003A12C8 /* icomoon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8C682611FD84CEF003A12C8 /* icomoon.ttf */; };
B8C682AC1FD8511D003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; };
B8C682AD1FD8511E003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; };
B8C682AE1FD8511F003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; };
@@ -564,7 +563,6 @@
B37C79D9BD0742CE936B6982 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
B88F58361FBF55E200B352B8 /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = ""; };
B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KeyboardTrackingView.xcodeproj; path = "../node_modules/react-native-keyboard-tracking-view/lib/KeyboardTrackingView.xcodeproj"; sourceTree = ""; };
- B8C682611FD84CEF003A12C8 /* icomoon.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = icomoon.ttf; path = ../resources/fonts/icomoon.ttf; sourceTree = ""; };
BAAE4B947F5D44959F0A9D5A /* libRNZeroconf.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNZeroconf.a; sourceTree = ""; };
C01CD6D4653143EEB5100C3A /* libRNI18n-tvOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libRNI18n-tvOS.a"; sourceTree = ""; };
C21010507E5B4B37BA0E4C9D /* RNAudio.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNAudio.xcodeproj; path = "../node_modules/react-native-audio/ios/RNAudio.xcodeproj"; sourceTree = ""; };
@@ -971,7 +969,6 @@
AF5E16F0398347E6A80C8CBE /* Resources */ = {
isa = PBXGroup;
children = (
- B8C682611FD84CEF003A12C8 /* icomoon.ttf */,
DC6EE17B5550465E98C70FF0 /* Entypo.ttf */,
A18EFC3B0CFE40E0918A8F0C /* EvilIcons.ttf */,
7A30DA4B2D474348824CD05B /* FontAwesome.ttf */,
@@ -1706,7 +1703,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- B8C682A81FD850F4003A12C8 /* icomoon.ttf in Resources */,
7A309C9C20724870000C6B13 /* Fabric.sh in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back.png b/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back.png
deleted file mode 100644
index 5357f91a..00000000
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back.png and /dev/null differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back@2x.png
deleted file mode 100644
index 4d21bc60..00000000
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back@2x.png and /dev/null differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back@3x.png
deleted file mode 100644
index f087b5ac..00000000
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/back@3x.png and /dev/null differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/Contents.json
similarity index 72%
rename from ios/RocketChatRN/Images.xcassets/Icons/back.imageset/Contents.json
rename to ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/Contents.json
index 3bad3d0c..ecaf3a2d 100644
--- a/ios/RocketChatRN/Images.xcassets/Icons/back.imageset/Contents.json
+++ b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/Contents.json
@@ -2,17 +2,17 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "back.png",
+ "filename" : "eye.png",
"scale" : "1x"
},
{
"idiom" : "universal",
- "filename" : "back@2x.png",
+ "filename" : "eye@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
- "filename" : "back@3x.png",
+ "filename" : "eye@3x.png",
"scale" : "3x"
}
],
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye.png b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye.png
new file mode 100644
index 00000000..34a12607
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye@2x.png
new file mode 100644
index 00000000..611c8aa0
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye@3x.png
new file mode 100644
index 00000000..e8d2a62d
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/eye.imageset/eye@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/Contents.json
new file mode 100644
index 00000000..da025da8
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "eye_slash.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "eye_slash@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "eye_slash@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash.png b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash.png
new file mode 100644
index 00000000..621162a1
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash@2x.png
new file mode 100644
index 00000000..6751bad0
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash@3x.png
new file mode 100644
index 00000000..ce3b4159
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/eye_slash.imageset/eye_slash@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag.png b/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag.png
index 890f02c7..e4a79ec3 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag.png and b/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@2x.png
index 70a5b3f6..d76738e3 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@2x.png and b/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@3x.png
index 8017c826..e083190f 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@3x.png and b/ios/RocketChatRN/Images.xcassets/Icons/hashtag.imageset/hashtag@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/Contents.json
new file mode 100644
index 00000000..71749ba7
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_facebook.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_facebook@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_facebook@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook.png
new file mode 100644
index 00000000..b362e2ff
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook@2x.png
new file mode 100644
index 00000000..e9543fd0
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook@3x.png
new file mode 100644
index 00000000..d6d01570
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_facebook.imageset/icon_facebook@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/Contents.json
new file mode 100644
index 00000000..650749f6
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_github.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_github@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_github@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github.png
new file mode 100644
index 00000000..af76b3fd
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github@2x.png
new file mode 100644
index 00000000..f92f531e
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github@3x.png
new file mode 100644
index 00000000..de753329
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_github.imageset/icon_github@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/Contents.json
new file mode 100644
index 00000000..9caf4068
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_gitlab.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_gitlab@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_gitlab@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab.png
new file mode 100644
index 00000000..908f0f31
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab@2x.png
new file mode 100644
index 00000000..92a633c8
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab@3x.png
new file mode 100644
index 00000000..fb8211a7
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_gitlab.imageset/icon_gitlab@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/Contents.json
new file mode 100644
index 00000000..b314b600
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_google.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_google@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_google@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google.png
new file mode 100644
index 00000000..77677544
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google@2x.png
new file mode 100644
index 00000000..4fb517cf
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google@3x.png
new file mode 100644
index 00000000..02342b21
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_google.imageset/icon_google@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/Contents.json
new file mode 100644
index 00000000..5614ec69
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_linkedin.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_linkedin@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_linkedin@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin.png
new file mode 100644
index 00000000..29cbe97f
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin@2x.png
new file mode 100644
index 00000000..e96d63de
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin@3x.png
new file mode 100644
index 00000000..674c9ef3
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_linkedin.imageset/icon_linkedin@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/Contents.json
new file mode 100644
index 00000000..0242ef13
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_meteor.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_meteor@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_meteor@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor.png
new file mode 100644
index 00000000..5489e031
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor@2x.png
new file mode 100644
index 00000000..25b8bc18
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor@3x.png
new file mode 100644
index 00000000..b7a0a845
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_meteor.imageset/icon_meteor@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/Contents.json
new file mode 100644
index 00000000..f2004903
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "icon_twitter.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_twitter@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "icon_twitter@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter.png
new file mode 100644
index 00000000..4570f1a5
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter@2x.png
new file mode 100644
index 00000000..7f8cf479
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter@3x.png
new file mode 100644
index 00000000..3faaeac1
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/icon_twitter.imageset/icon_twitter@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/Contents.json
new file mode 100644
index 00000000..562ad20c
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "key.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "key@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "key@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key.png b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key.png
new file mode 100644
index 00000000..b3132d59
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key@2x.png
new file mode 100644
index 00000000..face74d6
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key@3x.png
new file mode 100644
index 00000000..11206129
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/key.imageset/key@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock.png b/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock.png
index f0d911fd..a87c4495 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock.png and b/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@2x.png
index 7522c451..ba072523 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@2x.png and b/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@3x.png
index 72797f88..7b310acc 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@3x.png and b/ios/RocketChatRN/Images.xcassets/Icons/lock.imageset/lock@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/Contents.json
new file mode 100644
index 00000000..32899f8d
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "mail.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "mail@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "mail@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail.png b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail.png
new file mode 100644
index 00000000..de70ea51
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail@2x.png
new file mode 100644
index 00000000..2425cfa0
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail@3x.png
new file mode 100644
index 00000000..3b9ad1b6
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/mail.imageset/mail@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention.png b/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention.png
index 4455b43e..d02eef25 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention.png and b/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@2x.png
index 1f51d42f..3d64c502 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@2x.png and b/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@3x.png
index 29defc2c..3947ed67 100644
Binary files a/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@3x.png and b/ios/RocketChatRN/Images.xcassets/Icons/mention.imageset/mention@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/Contents.json
new file mode 100644
index 00000000..2ef36439
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "more.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "more@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "more@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more.png b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more.png
new file mode 100644
index 00000000..8aa0ed36
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more@2x.png
new file mode 100644
index 00000000..938f7428
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more@3x.png
new file mode 100644
index 00000000..3b998af6
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/more.imageset/more@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/Contents.json
new file mode 100644
index 00000000..4f881803
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "options.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "options@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "options@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options.png b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options.png
new file mode 100644
index 00000000..a2bd9ecd
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options@2x.png
new file mode 100644
index 00000000..612d02be
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options@3x.png
new file mode 100644
index 00000000..2622837a
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/options.imageset/options@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/Contents.json
new file mode 100644
index 00000000..f5572a29
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "user.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "user@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "user@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user.png b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user.png
new file mode 100644
index 00000000..4ea8662c
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user@2x.png
new file mode 100644
index 00000000..933f3b71
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user@3x.png
new file mode 100644
index 00000000..c8960e2b
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/user.imageset/user@3x.png differ
diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist
index 3c5c9889..71beab1c 100644
--- a/ios/RocketChatRN/Info.plist
+++ b/ios/RocketChatRN/Info.plist
@@ -82,13 +82,14 @@
Octicons.ttf
SimpleLineIcons.ttf
Zocial.ttf
- icomoon.ttf
Feather.ttf
UIRequiredDeviceCapabilities
armv7
+ UIStatusBarStyle
+ UIStatusBarStyleDefault
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
@@ -96,6 +97,6 @@
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
-
+
diff --git a/package-lock.json b/package-lock.json
index b1f64825..b8ea1670 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2154,7 +2154,7 @@
"integrity": "sha512-iOD1PRnTSVr9sDWQdesIpfRrwJhHfeEQe5BpalQxC5OhM9thpiE6cu2NlW1KBWl0RJG4ZiJaF1xLlCo9YxU6dA=="
},
"@rocket.chat/sdk": {
- "version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#0814dfc3650476801590d3fd8517632ef50b158c",
+ "version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#86d0b0f544ea700f742a66f59a21e1679aa7ff50",
"from": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#ddp",
"requires": {
"@types/lru-cache": "^4.1.0",
@@ -4749,6 +4749,12 @@
"integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==",
"dev": true
},
+ "binstring": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/binstring/-/binstring-0.2.1.tgz",
+ "integrity": "sha1-ihdNMB9tVO/aVQ3Zi7TLUk6s110=",
+ "dev": true
+ },
"bl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
@@ -6428,6 +6434,12 @@
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"dev": true
},
+ "convert-base": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/convert-base/-/convert-base-0.1.0.tgz",
+ "integrity": "sha1-jadOuY8ei53na9kqPc6X8dN/Mrc=",
+ "dev": true
+ },
"convert-source-map": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz",
@@ -10225,6 +10237,12 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
+ "hoek": {
+ "version": "3.0.4",
+ "resolved": "http://registry.npmjs.org/hoek/-/hoek-3.0.4.tgz",
+ "integrity": "sha1-Jorf9mu2aVxptHiaiLHghHw/MSM=",
+ "dev": true
+ },
"hoist-non-react-statics": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
@@ -11067,6 +11085,12 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
+ "isemail": {
+ "version": "2.2.1",
+ "resolved": "http://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz",
+ "integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=",
+ "dev": true
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -12760,6 +12784,18 @@
"merge-stream": "^1.0.1"
}
},
+ "joi": {
+ "version": "7.3.0",
+ "resolved": "http://registry.npmjs.org/joi/-/joi-7.3.0.tgz",
+ "integrity": "sha1-TZyfGBgwRECDZltbbNW4ymd5pek=",
+ "dev": true,
+ "requires": {
+ "hoek": "3.x.x",
+ "isemail": "2.x.x",
+ "moment": "2.x.x",
+ "topo": "2.x.x"
+ }
+ },
"js-base64": {
"version": "2.4.9",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz",
@@ -14863,6 +14899,23 @@
"os-tmpdir": "^1.0.0"
}
},
+ "otp.js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/otp.js/-/otp.js-1.1.0.tgz",
+ "integrity": "sha1-UCBUh7AcnlvvqgY0WHBMCVnDXVg=",
+ "dev": true,
+ "requires": {
+ "big-integer": "^1.6.9",
+ "binstring": "^0.2.1",
+ "convert-base": "^0.1.0",
+ "joi": "^7.0.1",
+ "pad-component": "0.0.1",
+ "qr-image": "^3.1.0",
+ "thirty-two": "^1.0.1",
+ "uid-safe": "^2.0.0",
+ "underscore": "^1.8.3"
+ }
+ },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -14923,6 +14976,12 @@
"thunkify": "^2.1.2"
}
},
+ "pad-component": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/pad-component/-/pad-component-0.0.1.tgz",
+ "integrity": "sha1-rR8izhvw/cDW3dkIrxfzUaQEuKw=",
+ "dev": true
+ },
"pako": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
@@ -16605,6 +16664,12 @@
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
"dev": true
},
+ "qr-image": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/qr-image/-/qr-image-3.2.0.tgz",
+ "integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug=",
+ "dev": true
+ },
"qs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
@@ -16691,6 +16756,12 @@
"ramda": "^0.24.1"
}
},
+ "random-bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+ "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=",
+ "dev": true
+ },
"randomatic": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz",
@@ -17208,9 +17279,9 @@
}
},
"react-native-gesture-handler": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.0.8.tgz",
- "integrity": "sha512-Lc6PV5nKXgZdDeky96yi6gAM1UJHaYwzZbZyph0YuSv/L6vTtN+KPGsKyIENoOyxLJ/i43MSNn7fR+Xbv0w/xA==",
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.0.9.tgz",
+ "integrity": "sha512-TPaiS8cAGqQA9p1GwjDbgMyNVCO/7cWC05h1rWIX2ZLoB1oSxGEBi5Es211HpBUmsTAzrUGH7M+ASGkAQeYH/A==",
"requires": {
"hoist-non-react-statics": "^2.3.1",
"invariant": "^2.2.2",
@@ -17336,9 +17407,9 @@
}
},
"react-is": {
- "version": "16.6.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.0.tgz",
- "integrity": "sha512-q8U7k0Fi7oxF1HvQgyBjPwDXeMplEsArnKt2iYhuIF86+GBbgLHdAmokL3XUFjTd7Q363OSNG55FOGUdONVn1g=="
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.1.tgz",
+ "integrity": "sha512-wOKsGtvTMYs7WAscmwwdM8sfRRvE17Ym30zFj3n37Qx5tHRfhenPKEPILHaHob6WoLFADmQm1ZNrE5xMCM6sCw=="
},
"react-lifecycles-compat": {
"version": "2.0.0",
@@ -20521,6 +20592,12 @@
}
}
},
+ "thirty-two": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
+ "integrity": "sha1-TKL//AKlEpDSdEueP1V2k8prYno=",
+ "dev": true
+ },
"throat": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
@@ -20632,6 +20709,23 @@
"resolved": "https://registry.npmjs.org/toml/-/toml-2.3.3.tgz",
"integrity": "sha512-O7L5hhSQHxuufWUdcTRPfuTh3phKfAZ/dqfxZFoxPCj2RYmpaSGLEIs016FCXItQwNr08yefUB5TSjzRYnajTA=="
},
+ "topo": {
+ "version": "2.0.2",
+ "resolved": "http://registry.npmjs.org/topo/-/topo-2.0.2.tgz",
+ "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x"
+ },
+ "dependencies": {
+ "hoek": {
+ "version": "4.2.1",
+ "resolved": "http://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
+ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
+ "dev": true
+ }
+ }
+ },
"toposort": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
@@ -20847,6 +20941,15 @@
}
}
},
+ "uid-safe": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "dev": true,
+ "requires": {
+ "random-bytes": "~1.0.0"
+ }
+ },
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
diff --git a/package.json b/package.json
index 9aa8efd9..fdcd3d97 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"react-native-dialog": "^5.4.0",
"react-native-fabric": "github:corymsmith/react-native-fabric#523a4edab3b2bf55ea9eeea2cf0dde82c5c29dd4",
"react-native-fast-image": "^5.0.11",
- "react-native-gesture-handler": "^1.0.8",
+ "react-native-gesture-handler": "^1.0.9",
"react-native-i18n": "^2.0.15",
"react-native-image-crop-picker": "git+https://github.com/RocketChat/react-native-image-crop-picker.git",
"react-native-image-zoom-viewer": "^2.2.21",
@@ -100,6 +100,7 @@
"jest-cli": "^23.6.0",
"metro-react-native-babel-preset": "^0.45.6",
"mocha": "^5.2.0",
+ "otp.js": "^1.1.0",
"react-dom": "16.6.0-alpha.8af6728",
"react-test-renderer": "16.6.0-alpha.8af6728",
"reactotron-react-native": "^2.1.0",
diff --git a/resources/fonts/icomoon.ttf b/resources/fonts/icomoon.ttf
deleted file mode 100755
index 3601ae80..00000000
Binary files a/resources/fonts/icomoon.ttf and /dev/null differ