Commit 2993bdea authored by Lucio Maciel's avatar Lucio Maciel

Make sure all client calls are guarded by try/catch

Use retryIO to retry client calls (still missing on some places)
Better error logging
NPE fixes.
parent dff97592
...@@ -18,10 +18,11 @@ import chat.rocket.android.app.migration.model.RealmUser ...@@ -18,10 +18,11 @@ import chat.rocket.android.app.migration.model.RealmUser
import chat.rocket.android.authentication.domain.model.toToken import chat.rocket.android.authentication.domain.model.toToken
import chat.rocket.android.dagger.DaggerAppComponent import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.widget.emoji.EmojiRepository import chat.rocket.android.widget.emoji.EmojiRepository
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
...@@ -148,12 +149,12 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -148,12 +149,12 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
private fun migrateServerInfo(url: String, authToken: String, settings: PublicSettings, user: RealmUser) { private fun migrateServerInfo(url: String, authToken: String, settings: PublicSettings, user: RealmUser) {
val userId = user._id val userId = user._id
val avatar = UrlHelper.getAvatarUrl(url, user.username!!) val avatar = url.avatarUrl(user.username!!)
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(url, it) url.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(url, it) url.serverLogoUrl(it)
} }
val account = Account(url, icon, logo, user.username!!, avatar) val account = Account(url, icon, logo, user.username!!, avatar)
launch(CommonPool) { launch(CommonPool) {
......
package chat.rocket.android.authentication.login.presentation package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.BuildConfig
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.OauthHelper import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatTwoFactorException import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.* import chat.rocket.core.internal.rest.*
import chat.rocket.core.model.Myself
import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.delay
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
...@@ -103,7 +100,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -103,7 +100,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private fun setupCasView() { private fun setupCasView() {
if (settings.isCasAuthenticationEnabled()) { if (settings.isCasAuthenticationEnabled()) {
val token = generateRandomString(17) val token = generateRandomString(17)
view.setupCasButtonListener(UrlHelper.getCasUrl(settings.casLoginUrl(), currentServer, token), token) view.setupCasButtonListener(settings.casLoginUrl().casUrl(currentServer, token), token)
view.showCasButton() view.showCasButton()
} }
} }
...@@ -118,7 +115,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -118,7 +115,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private fun setupOauthServicesView() { private fun setupOauthServicesView() {
launchUI(strategy) { launchUI(strategy) {
try { try {
val services = client.settingsOauth().services val services = retryIO("settingsOauth()") {
client.settingsOauth().services
}
if (services.isNotEmpty()) { if (services.isNotEmpty()) {
val state = "{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64() val state = "{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64()
var totalSocialAccountsEnabled = 0 var totalSocialAccountsEnabled = 0
...@@ -195,30 +194,32 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -195,30 +194,32 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
view.disableUserInput() view.disableUserInput()
view.showLoading() view.showLoading()
try { try {
val token = when (loginType) { val token = retryIO("login") {
TYPE_LOGIN_USER_EMAIL -> { when (loginType) {
if (usernameOrEmail.isEmail()) { TYPE_LOGIN_USER_EMAIL -> {
client.loginWithEmail(usernameOrEmail, password) if (usernameOrEmail.isEmail()) {
} else { client.loginWithEmail(usernameOrEmail, password)
if (settings.isLdapAuthenticationEnabled()) {
client.loginWithLdap(usernameOrEmail, password)
} else { } else {
client.login(usernameOrEmail, password) if (settings.isLdapAuthenticationEnabled()) {
client.loginWithLdap(usernameOrEmail, password)
} else {
client.login(usernameOrEmail, password)
}
} }
} }
} TYPE_LOGIN_CAS -> {
TYPE_LOGIN_CAS -> { delay(3, TimeUnit.SECONDS)
delay(3, TimeUnit.SECONDS) client.loginWithCas(credentialToken)
client.loginWithCas(credentialToken) }
} TYPE_LOGIN_OAUTH -> {
TYPE_LOGIN_OAUTH -> { client.loginWithOauth(credentialToken, credentialSecret)
client.loginWithOauth(credentialToken, credentialSecret) }
} else -> {
else -> { throw IllegalStateException("Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS or TYPE_LOGIN_OAUTH")
throw IllegalStateException("Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS or TYPE_LOGIN_OAUTH") }
} }
} }
val username = client.me().username val username = retryIO("me()") { client.me().username }
if (username != null) { if (username != null) {
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username)
saveAccount(username) saveAccount(username)
...@@ -258,12 +259,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -258,12 +259,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private suspend fun saveAccount(username: String) { private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, username) val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb) val account = Account(currentServer, icon, logo, username, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.registerusername.presentation ...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.registerusername.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -41,7 +43,9 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -41,7 +43,9 @@ class RegisterUsernamePresenter @Inject constructor(
if (NetworkHelper.hasInternetAccess()) { if (NetworkHelper.hasInternetAccess()) {
view.showLoading() view.showLoading()
try { try {
val me = client.updateOwnBasicInformation(username = username) val me = retryIO("updateOwnBasicInformation(username = $username)") {
client.updateOwnBasicInformation(username = username)
}
val registeredUsername = me.username val registeredUsername = me.username
if (registeredUsername != null) { if (registeredUsername != null) {
saveAccount(registeredUsername) saveAccount(registeredUsername)
...@@ -75,12 +79,12 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -75,12 +79,12 @@ class RegisterUsernamePresenter @Inject constructor(
private suspend fun saveAccount(username: String) { private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, username) val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb) val account = Account(currentServer, icon, logo, username, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -3,10 +3,10 @@ package chat.rocket.android.authentication.server.presentation ...@@ -3,10 +3,10 @@ package chat.rocket.android.authentication.server.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.GetAccountsInteractor import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.util.extensions.isValidUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import javax.inject.Inject import javax.inject.Inject
...@@ -18,7 +18,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView, ...@@ -18,7 +18,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor) { private val getAccountsInteractor: GetAccountsInteractor) {
fun connect(server: String) { fun connect(server: String) {
if (!UrlHelper.isValidUrl(server)) { if (!server.isValidUrl()) {
view.showInvalidServerUrlMessage() view.showInvalidServerUrlMessage()
} else { } else {
launchUI(strategy) { launchUI(strategy) {
......
...@@ -3,20 +3,22 @@ package chat.rocket.android.authentication.signup.presentation ...@@ -3,20 +3,22 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.viewmodel.NavHeaderViewModel
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.privacyPolicyUrl
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.extensions.termsOfServiceUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.registerPushToken
import chat.rocket.core.internal.rest.signup import chat.rocket.core.internal.rest.signup
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import javax.inject.Inject import javax.inject.Inject
...@@ -60,10 +62,10 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -60,10 +62,10 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
try { try {
// TODO This function returns a user so should we save it? // TODO This function returns a user so should we save it?
client.signup(email, name, username, password) retryIO("signup") { client.signup(email, name, username, password) }
// TODO This function returns a user token so should we save it? // TODO This function returns a user token so should we save it?
client.login(username, password) retryIO("login") { client.login(username, password) }
val me = client.me() val me = retryIO("me") { client.me() }
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me) saveAccount(me)
registerPushToken() registerPushToken()
...@@ -88,13 +90,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -88,13 +90,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
fun termsOfService() { fun termsOfService() {
serverInteractor.get()?.let { serverInteractor.get()?.let {
navigator.toWebPage(UrlHelper.getTermsOfServiceUrl(it)) navigator.toWebPage(it.termsOfServiceUrl())
} }
} }
fun privacyPolicy() { fun privacyPolicy() {
serverInteractor.get()?.let { serverInteractor.get()?.let {
navigator.toWebPage(UrlHelper.getPrivacyPolicyUrl(it)) navigator.toWebPage(it.privacyPolicyUrl())
} }
} }
...@@ -108,12 +110,12 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -108,12 +110,12 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
private suspend fun saveAccount(me: Myself) { private suspend fun saveAccount(me: Myself) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, me.username!!) val thumb = currentServer.avatarUrl(me.username!!)
val account = Account(currentServer, icon, logo, me.username!!, thumb) val account = Account(currentServer, icon, logo, me.username!!, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.twofactor.presentation ...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.twofactor.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -50,9 +52,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -50,9 +52,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
view.showLoading() view.showLoading()
try { try {
// The token is saved via the client TokenProvider // The token is saved via the client TokenProvider
val token = val token = retryIO("login") {
client.login(usernameOrEmail, password, twoFactorAuthenticationCode) client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
val me = client.me() }
val me = retryIO("me") { client.me() }
saveAccount(me) saveAccount(me)
tokenRepository.save(server, token) tokenRepository.save(server, token)
registerPushToken() registerPushToken()
...@@ -90,12 +93,12 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -90,12 +93,12 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private suspend fun saveAccount(me: Myself) { private suspend fun saveAccount(me: Myself) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, me.username!!) val thumb = currentServer.avatarUrl(me.username!!)
val account = Account(currentServer, icon, logo, me.username!!, thumb) val account = Account(currentServer, icon, logo, me.username!!, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -11,12 +11,13 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewM ...@@ -11,12 +11,13 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewM
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
...@@ -26,6 +27,7 @@ import chat.rocket.core.internal.realtime.State ...@@ -26,6 +27,7 @@ import chat.rocket.core.internal.realtime.State
import chat.rocket.core.internal.rest.* import chat.rocket.core.internal.rest.*
import chat.rocket.core.model.Command import chat.rocket.core.model.Command
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.Myself
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.android.UI import kotlinx.coroutines.experimental.android.UI
...@@ -40,7 +42,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -40,7 +42,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val navigator: ChatRoomNavigator, private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor, getSettingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor, private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val permissions: GetPermissionsInteractor, private val permissions: GetPermissionsInteractor,
private val uriInteractor: UriInteractor, private val uriInteractor: UriInteractor,
...@@ -68,7 +70,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -68,7 +70,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view.showLoading() view.showLoading()
try { try {
val messages = val messages =
client.messages(chatRoomId, roomTypeOf(chatRoomType), offset, 30).result retryIO(description = "messages chatRoom: $chatRoomId, type: $chatRoomType, offset: $offset") {
client.messages(chatRoomId, roomTypeOf(chatRoomType), offset, 30).result
}
messagesRepository.saveAll(messages) messagesRepository.saveAll(messages)
val messagesViewModels = mapper.map(messages) val messagesViewModels = mapper.map(messages)
...@@ -101,13 +105,16 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -101,13 +105,16 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view.disableSendMessageButton() view.disableSendMessageButton()
try { try {
// ignore message for now, will receive it on the stream // ignore message for now, will receive it on the stream
val message = if (messageId == null) { val message = retryIO {
client.sendMessage(chatRoomId, text) if (messageId == null) {
} else { client.sendMessage(chatRoomId, text)
client.updateMessage(chatRoomId, messageId, text) } else {
client.updateMessage(chatRoomId, messageId, text)
}
} }
view.clearMessageComposition() view.clearMessageComposition()
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error sending message...")
ex.message?.let { ex.message?.let {
view.showMessage(it) view.showMessage(it)
}.ifNull { }.ifNull {
...@@ -137,8 +144,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -137,8 +144,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fileSize > maxFileSize -> view.showInvalidFileSize(fileSize, maxFileSize) fileSize > maxFileSize -> view.showInvalidFileSize(fileSize, maxFileSize)
else -> { else -> {
Timber.d("Uploading to $roomId: $fileName - $mimeType") Timber.d("Uploading to $roomId: $fileName - $mimeType")
client.uploadFile(roomId, fileName!!, mimeType, msg, description = fileName) { retryIO("uploadFile($roomId, $fileName, $mimeType") {
uriInteractor.getInputStream(uri) client.uploadFile(roomId, fileName!!, mimeType, msg, description = fileName) {
uriInteractor.getInputStream(uri)
}
} }
} }
} }
...@@ -158,7 +167,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -158,7 +167,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private fun markRoomAsRead(roomId: String) { private fun markRoomAsRead(roomId: String) {
launchUI(strategy) { launchUI(strategy) {
try { try {
client.markAsRead(roomId) retryIO(description = "markAsRead($roomId)") { client.markAsRead(roomId) }
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
view.showMessage(ex.message!!) // TODO Remove. view.showMessage(ex.message!!) // TODO Remove.
Timber.e(ex) // FIXME: Right now we are only catching the exception with Timber. Timber.e(ex) // FIXME: Right now we are only catching the exception with Timber.
...@@ -204,23 +213,32 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -204,23 +213,32 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val roomType = roomTypeOf(chatRoomType!!) val roomType = roomTypeOf(chatRoomType!!)
messagesRepository.getByRoomId(chatRoomId!!) messagesRepository.getByRoomId(chatRoomId!!)
.sortedByDescending { it.timestamp }.firstOrNull()?.let { lastMessage -> .sortedByDescending { it.timestamp }.firstOrNull()?.let { lastMessage ->
val instant = Instant.ofEpochMilli(lastMessage.timestamp) val instant = Instant.ofEpochMilli(lastMessage.timestamp).toString()
val messages = client.history(chatRoomId!!, roomType, count = 50, try {
oldest = instant.toString()) val messages = retryIO(description = "history($chatRoomId, $roomType, $instant)") {
Timber.d("History: $messages") client.history(chatRoomId!!, roomType, count = 50,
oldest = instant)
}
Timber.d("History: $messages")
if (messages.result.isNotEmpty()) { if (messages.result.isNotEmpty()) {
val models = mapper.map(messages.result) val models = mapper.map(messages.result)
messagesRepository.saveAll(messages.result) messagesRepository.saveAll(messages.result)
launchUI(strategy) { launchUI(strategy) {
view.showNewMessage(models) view.showNewMessage(models)
} }
if (messages.result.size == 50) { if (messages.result.size == 50) {
// we loade at least count messages, try one more to fetch more messages // we loaded at least count messages, try one more to fetch more messages
loadMissingMessages() loadMissingMessages()
}
} }
} catch (ex: Exception) {
// TODO - we need to better treat connection problems here, but no let gaps
// on the messages list
Timber.d(ex, "Error fetching channel history")
ex.printStackTrace()
} }
} }
} }
...@@ -249,7 +267,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -249,7 +267,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
//TODO: Default delete message always to true. Until we have the permissions system //TODO: Default delete message always to true. Until we have the permissions system
//implemented, a user will only be able to delete his own messages. //implemented, a user will only be able to delete his own messages.
try { try {
client.deleteMessage(roomId, id, true) retryIO(description = "deleteMessage($roomId, $id)") {
client.deleteMessage(roomId, id, true)
}
// if Message_ShowDeletedStatus == true an update to that message will be dispatched. // if Message_ShowDeletedStatus == true an update to that message will be dispatched.
// Otherwise we signalize that we just want the message removed. // Otherwise we signalize that we just want the message removed.
if (!permissions.showDeletedStatus()) { if (!permissions.showDeletedStatus()) {
...@@ -272,14 +292,19 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -272,14 +292,19 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun citeMessage(roomType: String, roomName: String, messageId: String, mentionAuthor: Boolean) { fun citeMessage(roomType: String, roomName: String, messageId: String, mentionAuthor: Boolean) {
launchUI(strategy) { launchUI(strategy) {
val message = messagesRepository.getById(messageId) val message = messagesRepository.getById(messageId)
val me = client.me() //TODO: Cache this and use an interactor val me: Myself? = try {
val serverUrl = serverInteractor.get()!! retryIO("me()") { client.me() } //TODO: Cache this and use an interactor
} catch (ex: Exception) {
Timber.d(ex, "Error getting myself info.")
ex.printStackTrace()
null
}
message?.let { m -> message?.let { m ->
val id = m.id val id = m.id
val username = m.sender?.username val username = m.sender?.username
val user = "@" + if (settings.useRealName()) m.sender?.name val user = "@" + if (settings.useRealName()) m.sender?.name
?: m.sender?.username else m.sender?.username ?: m.sender?.username else m.sender?.username
val mention = if (mentionAuthor && me.username != username) user else "" val mention = if (mentionAuthor && me?.username != username) user else ""
val type = roomTypeOf(roomType) val type = roomTypeOf(roomType)
val room = when (type) { val room = when (type) {
is RoomType.Channel -> "channel" is RoomType.Channel -> "channel"
...@@ -290,7 +315,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -290,7 +315,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} }
view.showReplyingAction( view.showReplyingAction(
username = user, username = user,
replyMarkdown = "[ ]($serverUrl/$room/$roomName?msg=$id) $mention ", replyMarkdown = "[ ]($currentServer/$room/$roomName?msg=$id) $mention ",
quotedMessage = mapper.map(message).last().preview?.message ?: "" quotedMessage = mapper.map(message).last().preview?.message ?: ""
) )
} }
...@@ -338,7 +363,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -338,7 +363,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
return@launchUI return@launchUI
} }
try { try {
client.pinMessage(messageId) retryIO("pinMessage($messageId)") { client.pinMessage(messageId) }
} catch (e: RocketChatException) { } catch (e: RocketChatException) {
Timber.e(e) Timber.e(e)
} }
...@@ -352,7 +377,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -352,7 +377,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
return@launchUI return@launchUI
} }
try { try {
client.unpinMessage(messageId) retryIO("unpinMessage($messageId)") { client.unpinMessage(messageId) }
} catch (e: RocketChatException) { } catch (e: RocketChatException) {
Timber.e(e) Timber.e(e)
} }
...@@ -362,7 +387,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -362,7 +387,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun loadActiveMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0, filterSelfOut: Boolean = false) { fun loadActiveMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0, filterSelfOut: Boolean = false) {
launchUI(strategy) { launchUI(strategy) {
try { try {
val members = client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 50).result val members = retryIO("getMembers($chatRoomId, $chatRoomType, $offset)") {
client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 50).result
}
usersRepository.saveAll(members) usersRepository.saveAll(members)
val self = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY) val self = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
// Take at most the 100 most recent messages distinguished by user. Can return less. // Take at most the 100 most recent messages distinguished by user. Can return less.
...@@ -373,7 +400,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -373,7 +400,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val sender = it.sender!! val sender = it.sender!!
val username = sender.username ?: "" val username = sender.username ?: ""
val name = sender.name ?: "" val name = sender.name ?: ""
val avatarUrl = UrlHelper.getAvatarUrl(currentServer, username) val avatarUrl = currentServer.avatarUrl(username)
val found = members.firstOrNull { member -> member.username == username } val found = members.firstOrNull { member -> member.username == username }
val status = if (found != null) found.status else UserStatus.Offline() val status = if (found != null) found.status else UserStatus.Offline()
val searchList = mutableListOf(username, name) val searchList = mutableListOf(username, name)
...@@ -390,7 +417,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -390,7 +417,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
activeUsers.addAll(others.map { activeUsers.addAll(others.map {
val username = it.username ?: "" val username = it.username ?: ""
val name = it.name ?: "" val name = it.name ?: ""
val avatarUrl = UrlHelper.getAvatarUrl(currentServer, username) val avatarUrl = currentServer.avatarUrl(username)
val searchList = mutableListOf(username, name) val searchList = mutableListOf(username, name)
PeopleSuggestionViewModel(avatarUrl, username, username, name, it.status, true, searchList) PeopleSuggestionViewModel(avatarUrl, username, username, name, it.status, true, searchList)
}) })
...@@ -405,7 +432,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -405,7 +432,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun spotlight(query: String, @AutoCompleteType type: Int, filterSelfOut: Boolean = false) { fun spotlight(query: String, @AutoCompleteType type: Int, filterSelfOut: Boolean = false) {
launchUI(strategy) { launchUI(strategy) {
try { try {
val (users, rooms) = client.spotlight(query) val (users, rooms) = retryIO("spotlight($query)") { client.spotlight(query) }
when (type) { when (type) {
PEOPLE -> { PEOPLE -> {
if (users.isNotEmpty()) { if (users.isNotEmpty()) {
...@@ -417,7 +444,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -417,7 +444,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val name = it.name ?: "" val name = it.name ?: ""
val searchList = mutableListOf(username, name) val searchList = mutableListOf(username, name)
it.emails?.forEach { email -> searchList.add(email.address) } it.emails?.forEach { email -> searchList.add(email.address) }
PeopleSuggestionViewModel(UrlHelper.getAvatarUrl(currentServer, username), PeopleSuggestionViewModel(currentServer.avatarUrl(username),
username, username, name, it.status, false, searchList) username, username, name, it.status, false, searchList)
}.filterNot { filterSelfOut && self != null && self == it.text }) }.filterNot { filterSelfOut && self != null && self == it.text })
} }
...@@ -468,7 +495,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -468,7 +495,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun joinChat(chatRoomId: String) { fun joinChat(chatRoomId: String) {
launchUI(strategy) { launchUI(strategy) {
try { try {
client.joinChat(chatRoomId) retryIO("joinChat($chatRoomId)") { client.joinChat(chatRoomId) }
view.onJoined() view.onJoined()
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(ex) Timber.e(ex)
...@@ -482,7 +509,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -482,7 +509,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun react(messageId: String, emoji: String) { fun react(messageId: String, emoji: String) {
launchUI(strategy) { launchUI(strategy) {
try { try {
client.toggleReaction(messageId, emoji.removeSurrounding(":")) retryIO("toogleEmoji($messageId, $emoji)") {
client.toggleReaction(messageId, emoji.removeSurrounding(":"))
}
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(ex) Timber.e(ex)
} }
...@@ -497,7 +526,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -497,7 +526,9 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
launchUI(strategy) { launchUI(strategy) {
try { try {
//TODO: cache the commands //TODO: cache the commands
val commands = client.commands(0, 100).result val commands = retryIO("commands(0, 100)") {
client.commands(0, 100).result
}
view.populateCommandSuggestions(commands.map { view.populateCommandSuggestions(commands.map {
CommandSuggestionViewModel(it.command, it.description ?: "", listOf(it.command)) CommandSuggestionViewModel(it.command, it.description ?: "", listOf(it.command))
}) })
...@@ -516,13 +547,15 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -516,13 +547,15 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} else { } else {
val command = text.split(" ") val command = text.split(" ")
val name = command[0].substring(1) val name = command[0].substring(1)
var params: String = "" var params = ""
command.forEachIndexed { index, param -> command.forEachIndexed { index, param ->
if (index > 0) { if (index > 0) {
params += "$param " params += "$param "
} }
} }
val result = client.runCommand(Command(name, params), roomId) val result = retryIO("runCommand($name, $params, $roomId)") {
client.runCommand(Command(name, params), roomId)
}
if (!result) { if (!result) {
// failed, command is not valid so post it // failed, command is not valid so post it
sendMessage(roomId, text, null) sendMessage(roomId, text, null)
......
...@@ -9,9 +9,9 @@ import android.text.style.ForegroundColorSpan ...@@ -9,9 +9,9 @@ import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType import chat.rocket.core.model.MessageType
...@@ -235,7 +235,7 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -235,7 +235,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val username = message.sender?.username ?: "?" val username = message.sender?.username ?: "?"
return baseUrl?.let { return baseUrl?.let {
UrlHelper.getAvatarUrl(baseUrl, username) baseUrl.avatarUrl(username)
} }
} }
......
...@@ -12,6 +12,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory ...@@ -12,6 +12,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.chatRooms import chat.rocket.android.server.infraestructure.chatRooms
import chat.rocket.android.server.infraestructure.state import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
...@@ -94,7 +95,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -94,7 +95,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
try { try {
val roomList = getChatRoomsInteractor.getByName(currentServer, name) val roomList = getChatRoomsInteractor.getByName(currentServer, name)
if (roomList.isEmpty()) { if (roomList.isEmpty()) {
val (users, rooms) = client.spotlight(name) val (users, rooms) = retryIO("spotlight($name)") {
client.spotlight(name)
}
val chatRoomsCombined = mutableListOf<ChatRoom>() val chatRoomsCombined = mutableListOf<ChatRoom>()
chatRoomsCombined.addAll(usersToChatRooms(users)) chatRoomsCombined.addAll(usersToChatRooms(users))
chatRoomsCombined.addAll(roomsToChatRooms(rooms)) chatRoomsCombined.addAll(roomsToChatRooms(rooms))
...@@ -161,7 +164,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -161,7 +164,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
private suspend fun loadRooms(): List<ChatRoom> { private suspend fun loadRooms(): List<ChatRoom> {
val chatRooms = manager.chatRooms().update val chatRooms = retryIO("chatRooms") { manager.chatRooms().update }
val sortedRooms = sortRooms(chatRooms) val sortedRooms = sortRooms(chatRooms)
Timber.d("Loaded rooms: ${sortedRooms.size}") Timber.d("Loaded rooms: ${sortedRooms.size}")
saveChatRoomsInteractor.save(currentServer, sortedRooms) saveChatRoomsInteractor.save(currentServer, sortedRooms)
......
...@@ -12,11 +12,11 @@ import android.view.View ...@@ -12,11 +12,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.checkIfMyself import chat.rocket.android.infrastructure.checkIfMyself
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.setVisible
...@@ -75,7 +75,7 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -75,7 +75,7 @@ class ChatRoomsAdapter(private val context: Context,
private fun bindAvatar(chatRoom: ChatRoom, drawee: SimpleDraweeView) { private fun bindAvatar(chatRoom: ChatRoom, drawee: SimpleDraweeView) {
val avatarId = if (chatRoom.type is RoomType.DirectMessage) chatRoom.name else "@${chatRoom.name}" val avatarId = if (chatRoom.type is RoomType.DirectMessage) chatRoom.name else "@${chatRoom.name}"
drawee.setImageURI(UrlHelper.getAvatarUrl(chatRoom.client.url, avatarId)) drawee.setImageURI(chatRoom.client.url.avatarUrl(avatarId))
} }
private fun bindName(chatRoom: ChatRoom, textView: TextView) { private fun bindName(chatRoom: ChatRoom, textView: TextView) {
......
...@@ -158,15 +158,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -158,15 +158,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
listJob?.cancel() listJob?.cancel()
listJob = launch(UI) { listJob = launch(UI) {
val adapter = recycler_view.adapter as SimpleSectionedRecyclerViewAdapter val adapter = recycler_view.adapter as SimpleSectionedRecyclerViewAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android.dev/issues/5a90d4718cb3c2fa63b3f557?time=last-seven-days // FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android/issues/5ac2916c36c7b235275ccccf
// TODO - fix this bug to reenable DiffUtil // TODO - fix this bug to re-enable DiffUtil
val diff = async(CommonPool) { /*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet)) DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await() }.await()*/
if (isActive) { if (isActive) {
adapter.baseAdapter.updateRooms(newDataSet) adapter.baseAdapter.updateRooms(newDataSet)
diff.dispatchUpdatesTo(adapter) // TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
adapter.notifyDataSetChanged()
//Set sections always after data set is updated //Set sections always after data set is updated
setSections() setSections()
......
package chat.rocket.android.helper package chat.rocket.android.helper
import chat.rocket.android.util.extensions.removeTrailingSlash
object OauthHelper { object OauthHelper {
/** /**
...@@ -27,7 +29,7 @@ object OauthHelper { ...@@ -27,7 +29,7 @@ object OauthHelper {
fun getGoogleOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getGoogleOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://accounts.google.com/o/oauth2/v2/auth" + return "https://accounts.google.com/o/oauth2/v2/auth" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${UrlHelper.removeTrailingSlash(serverUrl)}/_oauth/google?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/google?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" + "&response_type=code" +
"&scope=email%20profile" "&scope=email%20profile"
...@@ -44,7 +46,7 @@ object OauthHelper { ...@@ -44,7 +46,7 @@ object OauthHelper {
fun getLinkedinOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getLinkedinOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://linkedin.com/oauth/v2/authorization" + return "https://linkedin.com/oauth/v2/authorization" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${UrlHelper.removeTrailingSlash(serverUrl)}/_oauth/linkedin?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/linkedin?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" "&response_type=code"
} }
...@@ -60,7 +62,7 @@ object OauthHelper { ...@@ -60,7 +62,7 @@ object OauthHelper {
fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://gitlab.com/oauth/authorize" + return "https://gitlab.com/oauth/authorize" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${UrlHelper.removeTrailingSlash(serverUrl)}/_oauth/gitlab?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" + "&response_type=code" +
"&scope=read_user" "&scope=read_user"
......
package chat.rocket.android.helper
import android.util.Patterns
object UrlHelper {
/**
* Returns the avatar URL.
*
* @param serverUrl The server URL.
* @param avatarName The avatar name.
* @return The avatar URL.
*/
fun getAvatarUrl(serverUrl: String, avatarName: String, format: String = "jpeg"): String =
removeTrailingSlash(serverUrl) + "/avatar/" + removeTrailingSlash(avatarName) + "?format=$format"
/**
* Returns the server logo URL.
*
* @param serverUrl The server URL.
* @param favicon The faviconLarge from the server settings.
* @return The server logo URL.
*/
fun getServerLogoUrl(serverUrl: String, favicon: String): String =
removeTrailingSlash(serverUrl) + "/$favicon"
/**
* Returns the CAS URL.
*
* @param casLoginUrl The CAS login URL from the server settings.
* @param serverUrl The server URL.
* @param token The token to be send to the CAS server.
* @return The avatar URL.
*/
fun getCasUrl(casLoginUrl: String, serverUrl: String, token: String): String =
removeTrailingSlash(casLoginUrl) + "?service=" + removeTrailingSlash(serverUrl) + "/_cas/" + token
/**
* Returns the server's Terms of Service URL.
*
* @param serverUrl The server URL.
* @return The server's Terms of Service URL.
*/
fun getTermsOfServiceUrl(serverUrl: String) = removeTrailingSlash(serverUrl) + "/terms-of-service"
/**
* Returns the server's Privacy Policy URL.
*
* @param serverUrl The server URL.
* @return The server's Privacy Policy URL.
*/
fun getPrivacyPolicyUrl(serverUrl: String) = removeTrailingSlash(serverUrl) + "/privacy-policy"
/**
* Returns an URL without trailing slash.
*
* @param serverUrl The URL to remove the trailing slash (if exists).
* @return An URL without trailing slash.
*/
fun removeTrailingSlash(serverUrl: String): String {
return if (serverUrl[serverUrl.length - 1] == '/') {
serverUrl.replace("/+$", "")
} else {
serverUrl
}
}
/**
* Checks if the given URL is valid or not.
* @param url The url to check its valid.
* @return True if url is valid, false otherwise.
*/
fun isValidUrl(url: String): Boolean = Patterns.WEB_URL.matcher(url).matches()
}
\ No newline at end of file
package chat.rocket.android.main.presentation package chat.rocket.android.main.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.viewmodel.NavHeaderViewModel import chat.rocket.android.main.viewmodel.NavHeaderViewModel
import chat.rocket.android.main.viewmodel.NavHeaderViewModelMapper import chat.rocket.android.main.viewmodel.NavHeaderViewModelMapper
...@@ -12,6 +11,8 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory ...@@ -12,6 +11,8 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -52,7 +53,7 @@ class MainPresenter @Inject constructor( ...@@ -52,7 +53,7 @@ class MainPresenter @Inject constructor(
checkServerInfo() checkServerInfo()
launchUI(strategy) { launchUI(strategy) {
try { try {
val me = client.me() val me = retryIO("me") { client.me() }
val model = navHeaderMapper.mapToViewModel(me) val model = navHeaderMapper.mapToViewModel(me)
saveAccount(model) saveAccount(model)
...@@ -77,7 +78,7 @@ class MainPresenter @Inject constructor( ...@@ -77,7 +78,7 @@ class MainPresenter @Inject constructor(
private suspend fun saveAccount(me: NavHeaderViewModel) { private suspend fun saveAccount(me: NavHeaderViewModel) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val account = Account(currentServer, icon, me.serverLogo, me.username, me.avatar) val account = Account(currentServer, icon, me.serverLogo, me.username, me.avatar)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
...@@ -90,7 +91,7 @@ class MainPresenter @Inject constructor( ...@@ -90,7 +91,7 @@ class MainPresenter @Inject constructor(
launchUI(strategy) { launchUI(strategy) {
try { try {
clearTokens() clearTokens()
client.logout() retryIO("logout") { client.logout() }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
Timber.d(exception, "Error calling logout") Timber.d(exception, "Error calling logout")
exception.message?.let { exception.message?.let {
...@@ -117,7 +118,11 @@ class MainPresenter @Inject constructor( ...@@ -117,7 +118,11 @@ class MainPresenter @Inject constructor(
serverInteractor.clear() serverInteractor.clear()
val pushToken = localRepository.get(LocalRepository.KEY_PUSH_TOKEN) val pushToken = localRepository.get(LocalRepository.KEY_PUSH_TOKEN)
if (pushToken != null) { if (pushToken != null) {
client.unregisterPushToken(pushToken) try {
retryIO("unregisterPushToken") { client.unregisterPushToken(pushToken) }
} catch (ex: Exception) {
Timber.d(ex, "Error unregistering push token")
}
} }
localRepository.clearAllFromServer(currentServer) localRepository.clearAllFromServer(currentServer)
} }
......
package chat.rocket.android.main.viewmodel package chat.rocket.android.main.viewmodel
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import javax.inject.Inject import javax.inject.Inject
...@@ -12,9 +13,9 @@ class NavHeaderViewModelMapper @Inject constructor(serverInteractor: GetCurrentS ...@@ -12,9 +13,9 @@ class NavHeaderViewModelMapper @Inject constructor(serverInteractor: GetCurrentS
fun mapToViewModel(me: Myself): NavHeaderViewModel { fun mapToViewModel(me: Myself): NavHeaderViewModel {
val username = mapUsername(me) val username = mapUsername(me)
val thumb = me.username?.let { UrlHelper.getAvatarUrl(currentServer, it) } val thumb = me.username?.let { currentServer.avatarUrl(it) }
val image = settings.wideTile() ?: settings.faviconLarge() val image = settings.wideTile() ?: settings.faviconLarge()
val logo = image?.let { UrlHelper.getServerLogoUrl(currentServer, it) } val logo = image?.let { currentServer.serverLogoUrl(it) }
return NavHeaderViewModel(username, currentServer, thumb, logo) return NavHeaderViewModel(username, currentServer, thumb, logo)
} }
......
...@@ -6,6 +6,7 @@ import chat.rocket.android.members.viewmodel.MemberViewModelMapper ...@@ -6,6 +6,7 @@ import chat.rocket.android.members.viewmodel.MemberViewModelMapper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -26,7 +27,9 @@ class MembersPresenter @Inject constructor(private val view: MembersView, ...@@ -26,7 +27,9 @@ class MembersPresenter @Inject constructor(private val view: MembersView,
try { try {
view.showLoading() view.showLoading()
val members = client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 60) val members = retryIO("getMembers($chatRoomId, $chatRoomType, $offset)") {
client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 60)
}
val memberViewModels = mapper.mapToViewModelList(members.result) val memberViewModels = mapper.mapToViewModelList(members.result)
view.showMembers(memberViewModels, members.total) view.showMembers(memberViewModels, members.total)
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
......
package chat.rocket.android.members.viewmodel package chat.rocket.android.members.viewmodel
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.common.model.User import chat.rocket.common.model.User
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
...@@ -25,7 +25,7 @@ class MemberViewModel(private val member: User, private val settings: Map<String ...@@ -25,7 +25,7 @@ class MemberViewModel(private val member: User, private val settings: Map<String
private fun getUserAvatar(): String? { private fun getUserAvatar(): String? {
val username = member.username ?: "?" val username = member.username ?: "?"
return baseUrl?.let { return baseUrl?.let {
UrlHelper.getAvatarUrl(baseUrl, username, "png") baseUrl.avatarUrl(username,"png")
} }
} }
......
package chat.rocket.android.profile.presentation package chat.rocket.android.profile.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
...@@ -25,9 +26,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView, ...@@ -25,9 +26,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
val myself = client.me() val myself = retryIO("me") { client.me() }
myselfId = myself.id myselfId = myself.id
val avatarUrl = UrlHelper.getAvatarUrl(serverUrl, myself.username!!) val avatarUrl = serverUrl.avatarUrl(myself.username!!)
view.showProfile( view.showProfile(
avatarUrl, avatarUrl,
myself.name ?: "", myself.name ?: "",
...@@ -51,9 +52,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView, ...@@ -51,9 +52,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
view.showLoading() view.showLoading()
try { try {
if(avatarUrl!="") { if(avatarUrl!="") {
client.setAvatar(avatarUrl) retryIO { client.setAvatar(avatarUrl) }
} }
val user = client.updateProfile(myselfId, email, name, username) val user = retryIO { client.updateProfile(myselfId, email, name, username) }
view.showProfileUpdateSuccessfullyMessage() view.showProfileUpdateSuccessfullyMessage()
loadUserProfile() loadUserProfile()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
......
...@@ -5,7 +5,6 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -5,7 +5,6 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
import com.google.android.gms.gcm.GoogleCloudMessaging import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID import com.google.android.gms.iid.InstanceID
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView ...@@ -5,6 +5,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.VersionInfo import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.serverInfo import chat.rocket.core.internal.rest.serverInfo
import timber.log.Timber import timber.log.Timber
...@@ -14,21 +15,25 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra ...@@ -14,21 +15,25 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
private val view: VersionCheckView) { private val view: VersionCheckView) {
internal fun checkServerInfo() { internal fun checkServerInfo() {
launchUI(strategy) { launchUI(strategy) {
val serverInfo = client.serverInfo() try {
val thisServerVersion = serverInfo.version val serverInfo = retryIO(description = "serverInfo", times = 5) { client.serverInfo() }
val isRequiredVersion = isRequiredServerVersion(thisServerVersion) val thisServerVersion = serverInfo.version
val isRecommendedVersion = isRecommendedServerVersion(thisServerVersion) val isRequiredVersion = isRequiredServerVersion(thisServerVersion)
if (isRequiredVersion) { val isRecommendedVersion = isRecommendedServerVersion(thisServerVersion)
if (isRecommendedVersion) { if (isRequiredVersion) {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)") if (isRecommendedVersion) {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)")
} else {
view.alertNotRecommendedVersion()
}
} else { } else {
view.alertNotRecommendedVersion() if (!isRecommendedVersion) {
} view.blockAndAlertNotRequiredVersion()
} else { Timber.i("Oops. Looks like your server is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!")
if (!isRecommendedVersion) { }
view.blockAndAlertNotRequiredVersion()
Timber.i("Oops. Looks like your server is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!")
} }
} catch (ex: Exception) {
Timber.d(ex, "Error getting server info")
} }
} }
} }
......
...@@ -4,6 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -4,6 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
...@@ -22,7 +23,10 @@ class PasswordPresenter @Inject constructor (private val view: PasswordView, ...@@ -22,7 +23,10 @@ class PasswordPresenter @Inject constructor (private val view: PasswordView,
try { try {
view.showLoading() view.showLoading()
client.updateProfile(client.me().id, null, null, password, null) val me = retryIO("me") { client.me() }
retryIO("updateProfile(${me.id})") {
client.updateProfile(me.id, null, null, password, null)
}
view.showPasswordSuccessfullyUpdatedMessage() view.showPasswordSuccessfullyUpdatedMessage()
view.hideLoading() view.hideLoading()
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.util.extensions ...@@ -2,6 +2,7 @@ package chat.rocket.android.util.extensions
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
...@@ -16,7 +17,9 @@ suspend fun RocketChatClient.registerPushToken( ...@@ -16,7 +17,9 @@ suspend fun RocketChatClient.registerPushToken(
launch(CommonPool) { launch(CommonPool) {
accounts.forEach { account -> accounts.forEach { account ->
try { try {
factory.create(account.serverUrl).registerPushToken(token) retryIO(description = "register push token: ${account.serverUrl}") {
factory.create(account.serverUrl).registerPushToken(token)
}
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error registering Push token for ${account.serverUrl}") Timber.d(ex, "Error registering Push token for ${account.serverUrl}")
ex.printStackTrace() ex.printStackTrace()
......
...@@ -29,14 +29,14 @@ fun Uri.getFileName(context: Context): String? { ...@@ -29,14 +29,14 @@ fun Uri.getFileName(context: Context): String? {
fun Uri.getFileSize(context: Context): Int { fun Uri.getFileSize(context: Context): Int {
val cursor = context.contentResolver.query(this, null, null, null, null, null) val cursor = context.contentResolver.query(this, null, null, null, null, null)
var fileSize: String? = null val fileSize = cursor?.use {
cursor.use { cursor ->
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
if (cursor != null && cursor.moveToFirst()) { if (cursor.moveToFirst()) {
if (!cursor.isNull(sizeIndex)) { if (!cursor.isNull(sizeIndex)) {
fileSize = cursor.getString(sizeIndex) return@use cursor.getString(sizeIndex)
} }
} }
return@use null
} }
return fileSize?.toIntOrNull() ?: -1 return fileSize?.toIntOrNull() ?: -1
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment