Unverified Commit 5382b1e5 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge branch 'develop' into fix/display-username

parents d00b8247 65ebc5bc
This diff is collapsed.
...@@ -61,7 +61,14 @@ interface Analytics { ...@@ -61,7 +61,14 @@ interface Analytics {
fun logServerSwitch(serverUrl: String, serverCount: Int) {} fun logServerSwitch(serverUrl: String, serverCount: Int) {}
/** /**
* Logs the admin opening. * Logs the admin opening event.
*/ */
fun logOpenAdmin() {} fun logOpenAdmin() {}
/**
* Logs the reset password event.
*
* @param resetPasswordSucceeded True if successful reset password, false otherwise.
*/
fun logResetPassword(resetPasswordSucceeded: Boolean) {}
} }
...@@ -70,4 +70,10 @@ class AnalyticsManager @Inject constructor( ...@@ -70,4 +70,10 @@ class AnalyticsManager @Inject constructor(
analytics.forEach { it.logOpenAdmin() } analytics.forEach { it.logOpenAdmin() }
} }
} }
fun logResetPassword(resetPasswordSucceeded: Boolean) {
if (analyticsTrackingInteractor.get()) {
analytics.forEach { it.logResetPassword(resetPasswordSucceeded) }
}
}
} }
...@@ -61,6 +61,10 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>( ...@@ -61,6 +61,10 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
reactionListener?.onReactionAdded(messageId, emoji) reactionListener?.onReactionAdded(messageId, emoji)
} }
} }
override fun onReactionLongClicked(shortname: String, isCustom: Boolean, url: String?, usernames: List<String>) {
reactionListener?.onReactionLongClicked(shortname, isCustom,url, usernames)
}
} }
val context = itemView.context val context = itemView.context
......
...@@ -75,7 +75,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -75,7 +75,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
class ReactionViewHolder( class ReactionViewHolder(
view: View, view: View,
private val listener: EmojiReactionListener? private val listener: EmojiReactionListener?
) : RecyclerView.ViewHolder(view), View.OnClickListener { ) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
@Inject @Inject
lateinit var localRepository: LocalRepository lateinit var localRepository: LocalRepository
...@@ -121,6 +121,8 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -121,6 +121,8 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
view_flipper_reaction.setOnClickListener(this@ReactionViewHolder) view_flipper_reaction.setOnClickListener(this@ReactionViewHolder)
text_count.setOnClickListener(this@ReactionViewHolder) text_count.setOnClickListener(this@ReactionViewHolder)
view_flipper_reaction.setOnLongClickListener(this@ReactionViewHolder)
text_count.setOnLongClickListener(this@ReactionViewHolder)
} }
} }
...@@ -132,6 +134,11 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -132,6 +134,11 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
} }
} }
} }
override fun onLongClick(v: View?): Boolean {
listener?.onReactionLongClicked(reaction.shortname, reaction.isCustom, reaction.url, reaction.usernames)
return true
}
} }
class AddReactionViewHolder( class AddReactionViewHolder(
......
...@@ -34,11 +34,11 @@ import chat.rocket.android.server.domain.uploadMimeTypeFilter ...@@ -34,11 +34,11 @@ import chat.rocket.android.server.domain.uploadMimeTypeFilter
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
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.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.getByteArray import chat.rocket.android.util.extension.getByteArray
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.getBitmpap import chat.rocket.android.util.extensions.exhaustive
import chat.rocket.android.util.retryDB
import chat.rocket.android.util.retryIO 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
...@@ -82,7 +82,6 @@ import kotlinx.coroutines.experimental.launch ...@@ -82,7 +82,6 @@ import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import org.threeten.bp.Instant import org.threeten.bp.Instant
import timber.log.Timber import timber.log.Timber
import java.io.InputStream
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
...@@ -138,9 +137,12 @@ class ChatRoomPresenter @Inject constructor( ...@@ -138,9 +137,12 @@ class ChatRoomPresenter @Inject constructor(
} finally { } finally {
// User has at least an 'owner' or 'moderator' role. // User has at least an 'owner' or 'moderator' role.
val userCanMod = isOwnerOrMod() val userCanMod = isOwnerOrMod()
val chatRoom = dbManager.getRoom(roomId)
val muted = chatRoom?.chatRoom?.muted ?: emptyList()
// Can post anyway if has the 'post-readonly' permission on server. // Can post anyway if has the 'post-readonly' permission on server.
val userCanPost = userCanMod || permissions.canPostToReadOnlyChannels() val userCanPost = userCanMod || permissions.canPostToReadOnlyChannels() ||
chatIsBroadcast = dbManager.getRoom(roomId)?.chatRoom?.run { !muted.contains(currentLoggedUsername)
chatIsBroadcast = chatRoom?.chatRoom?.run {
broadcast broadcast
} ?: false } ?: false
view.onRoomUpdated(userCanPost, chatIsBroadcast, userCanMod) view.onRoomUpdated(userCanPost, chatIsBroadcast, userCanMod)
...@@ -901,7 +903,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -901,7 +903,8 @@ class ChatRoomPresenter @Inject constructor(
// TODO: move this to new interactor or FetchChatRoomsInteractor? // TODO: move this to new interactor or FetchChatRoomsInteractor?
private suspend fun getChatRoomAsync(roomId: String): ChatRoom? = withContext(CommonPool) { private suspend fun getChatRoomAsync(roomId: String): ChatRoom? = withContext(CommonPool) {
return@withContext dbManager.chatRoomDao().get(roomId)?.let { retryDB("getRoom($roomId)") {
dbManager.chatRoomDao().get(roomId)?.let {
with(it.chatRoom) { with(it.chatRoom) {
ChatRoom( ChatRoom(
id = id, id = id,
...@@ -934,10 +937,12 @@ class ChatRoomPresenter @Inject constructor( ...@@ -934,10 +937,12 @@ class ChatRoomPresenter @Inject constructor(
} }
} }
} }
}
// TODO: move this to new interactor or FetchChatRoomsInteractor? // TODO: move this to new interactor or FetchChatRoomsInteractor?
private suspend fun getChatRoomsAsync(name: String? = null): List<ChatRoom> = withContext(CommonPool) { private suspend fun getChatRoomsAsync(name: String? = null): List<ChatRoom> = withContext(CommonPool) {
return@withContext dbManager.chatRoomDao().getAllSync().filter { retryDB("getAllSync()") {
dbManager.chatRoomDao().getAllSync().filter {
if (name == null) { if (name == null) {
return@filter true return@filter true
} }
...@@ -975,6 +980,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -975,6 +980,7 @@ class ChatRoomPresenter @Inject constructor(
} }
} }
} }
}
fun joinChat(chatRoomId: String) { fun joinChat(chatRoomId: String) {
launchUI(strategy) { launchUI(strategy) {
......
...@@ -60,6 +60,7 @@ import chat.rocket.android.emoji.EmojiKeyboardPopup ...@@ -60,6 +60,7 @@ import chat.rocket.android.emoji.EmojiKeyboardPopup
import chat.rocket.android.emoji.EmojiParser import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiPickerPopup import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.emoji.internal.GlideApp
import chat.rocket.android.emoji.internal.isCustom import chat.rocket.android.emoji.internal.isCustom
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.ImageHelper import chat.rocket.android.helper.ImageHelper
...@@ -84,6 +85,8 @@ import dagger.android.support.AndroidSupportInjection ...@@ -84,6 +85,8 @@ import dagger.android.support.AndroidSupportInjection
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.emoji_image_row_item.*
import kotlinx.android.synthetic.main.emoji_row_item.*
import kotlinx.android.synthetic.main.fragment_chat_room.* import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_attachment_options.* import kotlinx.android.synthetic.main.message_attachment_options.*
import kotlinx.android.synthetic.main.message_composer.* import kotlinx.android.synthetic.main.message_composer.*
...@@ -91,6 +94,7 @@ import kotlinx.android.synthetic.main.message_list.* ...@@ -91,6 +94,7 @@ import kotlinx.android.synthetic.main.message_list.*
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import kotlinx.android.synthetic.main.reaction_praises_list_item.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
...@@ -611,7 +615,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -611,7 +615,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun showGenericErrorMessage(){ override fun showGenericErrorMessage() {
ui { ui {
showMessage(getString(R.string.msg_generic_error)) showMessage(getString(R.string.msg_generic_error))
} }
...@@ -688,6 +692,44 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -688,6 +692,44 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.react(messageId, emoji.shortname) presenter.react(messageId, emoji.shortname)
} }
override fun onReactionLongClicked(shortname: String, isCustom: Boolean, url: String?, usernames: List<String>) {
val layout = LayoutInflater.from(requireContext()).inflate(R.layout.reaction_praises_list_item, null)
val dialog = AlertDialog.Builder(requireContext())
.setView(layout)
.setCancelable(true)
with(layout) {
view_flipper.displayedChild = if (isCustom) 1 else 0
if (isCustom && url != null) {
val glideRequest = if (url.endsWith("gif", true)) {
GlideApp.with(requireContext()).asGif()
} else {
GlideApp.with(requireContext()).asBitmap()
}
glideRequest.load(url).into(emoji_image_view)
} else {
emoji_view.text = EmojiParser.parse(requireContext(), shortname)
}
var listing = ""
if (usernames.size == 1) {
listing = usernames.first()
} else {
usernames.forEachIndexed { index, username ->
listing += if (index == usernames.size - 1) "|$username" else "$username, "
}
listing = listing.replace(", |", " ${requireContext().getString(R.string.msg_and)} ")
}
text_view_usernames.text = requireContext().resources.getQuantityString(
R.plurals.msg_reacted_with_, usernames.size, listing, shortname)
dialog.show()
}
}
override fun showReactionsPopup(messageId: String) { override fun showReactionsPopup(messageId: String) {
ui { ui {
val emojiPickerPopup = EmojiPickerPopup(it) val emojiPickerPopup = EmojiPickerPopup(it)
...@@ -787,9 +829,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -787,9 +829,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
private fun setupMessageComposer(canPost: Boolean) { private fun setupMessageComposer(canPost: Boolean) {
if (isReadOnly && !canPost) { if (!canPost) {
text_room_is_read_only.isVisible = true text_room_is_read_only.isVisible = true
input_container.isVisible = false input_container.isVisible = false
text_room_is_read_only.setText(
if (isReadOnly) {
R.string.msg_this_room_is_read_only
} else {
// Not a read-only channel but user has been muted.
R.string.msg_muted_on_this_channel
}
)
} else if (!isSubscribed && roomTypeOf(chatRoomType) !is RoomType.DirectMessage) { } else if (!isSubscribed && roomTypeOf(chatRoomType) !is RoomType.DirectMessage) {
input_container.isVisible = false input_container.isVisible = false
button_join_chat.isVisible = true button_join_chat.isVisible = true
......
...@@ -6,5 +6,6 @@ data class ReactionUiModel( ...@@ -6,5 +6,6 @@ data class ReactionUiModel(
val unicode: CharSequence, val unicode: CharSequence,
val count: Int, val count: Int,
val usernames: List<String> = emptyList(), val usernames: List<String> = emptyList(),
var url: String? = null var url: String? = null,
val isCustom: Boolean = false
) )
...@@ -19,6 +19,7 @@ import chat.rocket.android.dagger.scope.PerFragment ...@@ -19,6 +19,7 @@ import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.emoji.EmojiParser import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiRepository import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.emoji.internal.isCustom
import chat.rocket.android.helper.MessageHelper import chat.rocket.android.helper.MessageHelper
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.helper.UserHelper import chat.rocket.android.helper.UserHelper
...@@ -31,6 +32,7 @@ import chat.rocket.android.server.domain.messageReadReceiptEnabled ...@@ -31,6 +32,7 @@ import chat.rocket.android.server.domain.messageReadReceiptEnabled
import chat.rocket.android.server.domain.messageReadReceiptStoreUsers import chat.rocket.android.server.domain.messageReadReceiptStoreUsers
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.orFalse
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.ifNotNullNorEmpty import chat.rocket.android.util.extensions.ifNotNullNorEmpty
import chat.rocket.android.util.extensions.isNotNullNorEmpty import chat.rocket.android.util.extensions.isNotNullNorEmpty
...@@ -469,7 +471,7 @@ class UiModelMapper @Inject constructor( ...@@ -469,7 +471,7 @@ class UiModelMapper @Inject constructor(
val list = mutableListOf<ReactionUiModel>() val list = mutableListOf<ReactionUiModel>()
val customEmojis = EmojiRepository.getCustomEmojis() val customEmojis = EmojiRepository.getCustomEmojis()
it.getShortNames().forEach { shortname -> it.getShortNames().forEach { shortname ->
val usernames = it.getUsernames(shortname) ?: emptyList() val usernames = it.getUsernames(shortname).orEmpty()
val count = usernames.size val count = usernames.size
val custom = customEmojis.firstOrNull { emoji -> emoji.shortname == shortname } val custom = customEmojis.firstOrNull { emoji -> emoji.shortname == shortname }
list.add( list.add(
...@@ -478,7 +480,8 @@ class UiModelMapper @Inject constructor( ...@@ -478,7 +480,8 @@ class UiModelMapper @Inject constructor(
unicode = EmojiParser.parse(context, shortname), unicode = EmojiParser.parse(context, shortname),
count = count, count = count,
usernames = usernames, usernames = usernames,
url = custom?.url) url = custom?.url,
isCustom = custom != null)
) )
} }
list list
......
...@@ -14,5 +14,6 @@ data class RoomUiModel( ...@@ -14,5 +14,6 @@ data class RoomUiModel(
val alert: Boolean = false, val alert: Boolean = false,
val lastMessage: CharSequence? = null, val lastMessage: CharSequence? = null,
val status: UserStatus? = null, val status: UserStatus? = null,
val username: String? = null val username: String? = null,
val muted: List<String> = emptyList()
) )
...@@ -3,9 +3,12 @@ package chat.rocket.android.chatrooms.infrastructure ...@@ -3,9 +3,12 @@ package chat.rocket.android.chatrooms.infrastructure
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.model.ChatRoom import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.util.retryDB
import javax.inject.Inject import javax.inject.Inject
class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao) {
// TODO - check how to use retryDB here - suspend
fun getChatRooms(order: Order): LiveData<List<ChatRoom>> { fun getChatRooms(order: Order): LiveData<List<ChatRoom>> {
return when(order) { return when(order) {
Order.ACTIVITY -> dao.getAll() Order.ACTIVITY -> dao.getAll()
...@@ -15,9 +18,10 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){ ...@@ -15,9 +18,10 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){
} }
} }
fun search(query: String) = dao.searchSync(query) suspend fun search(query: String) =
retryDB("roomSearch($query)") { dao.searchSync(query) }
fun count() = dao.count() suspend fun count() = retryDB("roomsCount") { dao.count() }
enum class Order { enum class Order {
ACTIVITY, ACTIVITY,
......
...@@ -13,6 +13,7 @@ import chat.rocket.android.server.domain.useRealName ...@@ -13,6 +13,7 @@ import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.domain.useSpecialCharsOnRoom import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.server.infraestructure.ConnectionManager import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.android.util.retryIO 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
...@@ -41,11 +42,31 @@ class ChatRoomsPresenter @Inject constructor( ...@@ -41,11 +42,31 @@ class ChatRoomsPresenter @Inject constructor(
private val client = manager.client private val client = manager.client
private val settings = settingsRepository.get(currentServer) private val settings = settingsRepository.get(currentServer)
fun loadChatRoom(roomId: String) {
launchUI(strategy) {
view.showLoadingRoom("")
try {
val room = dbManager.getRoom(roomId)
if (room != null) {
loadChatRoom(room.chatRoom, true)
} else {
Timber.d("Error loading channel")
view.showGenericErrorMessage()
}
} catch (ex: Exception) {
Timber.d(ex, "Error loading channel")
view.showGenericErrorMessage()
} finally {
view.hideLoadingRoom()
}
}
}
fun loadChatRoom(chatRoom: RoomUiModel) { fun loadChatRoom(chatRoom: RoomUiModel) {
launchUI(strategy) { launchUI(strategy) {
view.showLoadingRoom(chatRoom.name) view.showLoadingRoom(chatRoom.name)
try { try {
val room = dbManager.getRoom(chatRoom.id) val room = retryDB("getRoom(${chatRoom.id}") { dbManager.getRoom(chatRoom.id) }
if (room != null) { if (room != null) {
loadChatRoom(room.chatRoom, true) loadChatRoom(room.chatRoom, true)
} else { } else {
...@@ -56,7 +77,8 @@ class ChatRoomsPresenter @Inject constructor( ...@@ -56,7 +77,8 @@ class ChatRoomsPresenter @Inject constructor(
type = type.toString(), type = type.toString(),
name = username ?: name.toString(), name = username ?: name.toString(),
fullname = name.toString(), fullname = name.toString(),
open = open open = open,
muted = muted
) )
loadChatRoom(entity, false) loadChatRoom(entity, false)
} }
......
...@@ -84,8 +84,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -84,8 +84,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
if (bundle != null) { if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId?.let { chatRoomId?.let {
// TODO - bring back support to load a room from id. presenter.loadChatRoom(it)
//presenter.goToChatRoomWithId(it)
chatRoomId = null chatRoomId = null
} }
} }
......
...@@ -27,7 +27,7 @@ import kotlinx.coroutines.experimental.launch ...@@ -27,7 +27,7 @@ import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.newSingleThreadContext import kotlinx.coroutines.experimental.newSingleThreadContext
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import timber.log.Timber import timber.log.Timber
import java.security.InvalidParameterException import java.lang.IllegalArgumentException
import kotlin.coroutines.experimental.coroutineContext import kotlin.coroutines.experimental.coroutineContext
...@@ -171,6 +171,6 @@ fun Query.asSortingOrder(): ChatRoomsRepository.Order { ...@@ -171,6 +171,6 @@ fun Query.asSortingOrder(): ChatRoomsRepository.Order {
ChatRoomsRepository.Order.ACTIVITY ChatRoomsRepository.Order.ACTIVITY
} }
} }
else -> throw InvalidParameterException("Should be ByName or ByActivity") else -> throw IllegalArgumentException("Should be ByName or ByActivity")
} }
} }
...@@ -19,6 +19,7 @@ import chat.rocket.android.util.extensions.exhaustive ...@@ -19,6 +19,7 @@ import chat.rocket.android.util.extensions.exhaustive
import chat.rocket.android.util.extensions.removeTrailingSlash import chat.rocket.android.util.extensions.removeTrailingSlash
import chat.rocket.android.util.extensions.toEntity import chat.rocket.android.util.extensions.toEntity
import chat.rocket.android.util.extensions.userId import chat.rocket.android.util.extensions.userId
import chat.rocket.android.util.retryDB
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
...@@ -41,8 +42,7 @@ import timber.log.Timber ...@@ -41,8 +42,7 @@ import timber.log.Timber
import java.util.HashSet import java.util.HashSet
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
class DatabaseManager(val context: Application, class DatabaseManager(val context: Application, val serverUrl: String) {
val serverUrl: String) {
private val database: RCDatabase = androidx.room.Room.databaseBuilder(context, private val database: RCDatabase = androidx.room.Room.databaseBuilder(context,
RCDatabase::class.java, serverUrl.databaseName()) RCDatabase::class.java, serverUrl.databaseName())
...@@ -92,13 +92,15 @@ class DatabaseManager(val context: Application, ...@@ -92,13 +92,15 @@ class DatabaseManager(val context: Application,
} }
} }
fun logout() { suspend fun logout() {
database.clearAllTables() retryDB("clearAllTables") { database.clearAllTables() }
} }
suspend fun getRoom(id: String) = withContext(dbManagerContext) { suspend fun getRoom(id: String) = withContext(dbManagerContext) {
retryDB("getRoom($id)") {
chatRoomDao().get(id) chatRoomDao().get(id)
} }
}
fun processUsersBatch(users: List<User>) { fun processUsersBatch(users: List<User>) {
launch(dbManagerContext) { launch(dbManagerContext) {
...@@ -124,11 +126,11 @@ class DatabaseManager(val context: Application, ...@@ -124,11 +126,11 @@ class DatabaseManager(val context: Application,
val toInsert = ArrayList<ChatRoomEntity>(batch.size / 2) val toInsert = ArrayList<ChatRoomEntity>(batch.size / 2)
val toUpdate = ArrayList<ChatRoomEntity>(batch.size) val toUpdate = ArrayList<ChatRoomEntity>(batch.size)
batch.forEach { batch.forEach {
when(it.type) { when (it.type) {
is Type.Removed -> toRemove.add(removeChatRoom(it.data)) is Type.Removed -> toRemove.add(removeChatRoom(it.data))
is Type.Inserted -> insertChatRoom(it.data)?.let { toInsert.add(it) } is Type.Inserted -> insertChatRoom(it.data)?.let { toInsert.add(it) }
is Type.Updated -> { is Type.Updated -> {
when(it.data) { when (it.data) {
is Subscription -> updateSubs[(it.data as Subscription).roomId] = it.data as Subscription is Subscription -> updateSubs[(it.data as Subscription).roomId] = it.data as Subscription
is Room -> updateRooms[(it.data as Room).id] = it.data as Room is Room -> updateRooms[(it.data as Room).id] = it.data as Room
} }
...@@ -152,7 +154,7 @@ class DatabaseManager(val context: Application, ...@@ -152,7 +154,7 @@ class DatabaseManager(val context: Application,
fun updateSelfUser(myself: Myself) { fun updateSelfUser(myself: Myself) {
launch(dbManagerContext) { launch(dbManagerContext) {
val user = userDao().getUser(myself.id) val user = retryDB("getUser(${myself.id})") { userDao().getUser(myself.id) }
val entity = user?.copy( val entity = user?.copy(
name = myself.name ?: user.name, name = myself.name ?: user.name,
username = myself.username ?: user.username, username = myself.username ?: user.username,
...@@ -329,14 +331,14 @@ class DatabaseManager(val context: Application, ...@@ -329,14 +331,14 @@ class DatabaseManager(val context: Application,
} }
private fun removeChatRoom(data: BaseRoom): String { private fun removeChatRoom(data: BaseRoom): String {
return when(data) { return when (data) {
is Subscription -> data.roomId is Subscription -> data.roomId
else -> data.id else -> data.id
} }
} }
private suspend fun updateRoom(data: Room): ChatRoomEntity? { private suspend fun updateRoom(data: Room): ChatRoomEntity? {
return chatRoomDao().get(data.id)?.let { current -> return retryDB("getChatRoom(${data.id})") { chatRoomDao().get(data.id) }?.let { current ->
with(data) { with(data) {
val chatRoom = current.chatRoom val chatRoom = current.chatRoom
...@@ -351,7 +353,8 @@ class DatabaseManager(val context: Application, ...@@ -351,7 +353,8 @@ class DatabaseManager(val context: Application,
updatedAt = updatedAt ?: chatRoom.updatedAt, updatedAt = updatedAt ?: chatRoom.updatedAt,
lastMessageText = mapLastMessageText(lastMessage), lastMessageText = mapLastMessageText(lastMessage),
lastMessageUserId = lastMessage?.sender?.id, lastMessageUserId = lastMessage?.sender?.id,
lastMessageTimestamp = lastMessage?.timestamp lastMessageTimestamp = lastMessage?.timestamp,
muted = muted
) )
} }
} }
...@@ -373,7 +376,7 @@ class DatabaseManager(val context: Application, ...@@ -373,7 +376,7 @@ class DatabaseManager(val context: Application,
context.getString(R.string.msg_sent_attachment) context.getString(R.string.msg_sent_attachment)
private suspend fun updateSubscription(data: Subscription): ChatRoomEntity? { private suspend fun updateSubscription(data: Subscription): ChatRoomEntity? {
return chatRoomDao().get(data.roomId)?.let { current -> return retryDB("getRoom(${data.roomId}") { chatRoomDao().get(data.roomId) }?.let { current ->
with(data) { with(data) {
val userId = if (type is RoomType.DirectMessage) { val userId = if (type is RoomType.DirectMessage) {
...@@ -389,7 +392,8 @@ class DatabaseManager(val context: Application, ...@@ -389,7 +392,8 @@ class DatabaseManager(val context: Application,
id = roomId, id = roomId,
subscriptionId = id, subscriptionId = id,
type = type.toString(), type = type.toString(),
name = name ?: throw NullPointerException(), // this should be filtered on the SDK name = name
?: throw NullPointerException(), // this should be filtered on the SDK
fullname = fullName ?: chatRoom.fullname, fullname = fullName ?: chatRoom.fullname,
userId = userId ?: chatRoom.userId, userId = userId ?: chatRoom.userId,
readonly = readonly ?: chatRoom.readonly, readonly = readonly ?: chatRoom.readonly,
...@@ -402,14 +406,15 @@ class DatabaseManager(val context: Application, ...@@ -402,14 +406,15 @@ class DatabaseManager(val context: Application,
groupMentions = groupMentions ?: chatRoom.groupMentions, groupMentions = groupMentions ?: chatRoom.groupMentions,
updatedAt = updatedAt ?: chatRoom.updatedAt, updatedAt = updatedAt ?: chatRoom.updatedAt,
timestamp = timestamp ?: chatRoom.timestamp, timestamp = timestamp ?: chatRoom.timestamp,
lastSeen = lastSeen ?: chatRoom.lastSeen lastSeen = lastSeen ?: chatRoom.lastSeen,
muted = chatRoom.muted
) )
} }
} }
} }
private suspend fun insertChatRoom(data: BaseRoom): ChatRoomEntity? { private suspend fun insertChatRoom(data: BaseRoom): ChatRoomEntity? {
return when(data) { return when (data) {
is Room -> insertRoom(data) is Room -> insertRoom(data)
is Subscription -> insertSubscription(data) is Subscription -> insertSubscription(data)
else -> null else -> null
...@@ -451,7 +456,8 @@ class DatabaseManager(val context: Application, ...@@ -451,7 +456,8 @@ class DatabaseManager(val context: Application,
id = room.id, id = room.id,
subscriptionId = subscription.id, subscriptionId = subscription.id,
type = room.type.toString(), type = room.type.toString(),
name = room.name ?: subscription.name ?: throw NullPointerException(), // this should be filtered on the SDK name = room.name ?: subscription.name
?: throw NullPointerException(), // this should be filtered on the SDK
fullname = subscription.fullName ?: room.fullName, fullname = subscription.fullName ?: room.fullName,
userId = userId, userId = userId,
ownerId = room.user?.id, ownerId = room.user?.id,
...@@ -506,7 +512,8 @@ class DatabaseManager(val context: Application, ...@@ -506,7 +512,8 @@ class DatabaseManager(val context: Application,
lastMessageText = mapLastMessageText(lastMessage), lastMessageText = mapLastMessageText(lastMessage),
lastMessageUserId = lastMessage?.sender?.id, lastMessageUserId = lastMessage?.sender?.id,
lastMessageTimestamp = lastMessage?.timestamp, lastMessageTimestamp = lastMessage?.timestamp,
broadcast = broadcast broadcast = broadcast,
muted = room.muted
) )
} }
} }
...@@ -535,9 +542,11 @@ class DatabaseManager(val context: Application, ...@@ -535,9 +542,11 @@ class DatabaseManager(val context: Application,
} }
} }
private fun findUser(userId: String): String? = userDao().findUser(userId) private suspend fun findUser(userId: String): String? =
retryDB("findUser($userId)") { userDao().findUser(userId) }
private fun doOperation(operation: Operation) { private suspend fun doOperation(operation: Operation) {
retryDB(description = "doOperation($operation)") {
when (operation) { when (operation) {
is Operation.ClearStatus -> userDao().clearStatus() is Operation.ClearStatus -> userDao().clearStatus()
is Operation.UpdateRooms -> { is Operation.UpdateRooms -> {
...@@ -569,6 +578,7 @@ class DatabaseManager(val context: Application, ...@@ -569,6 +578,7 @@ class DatabaseManager(val context: Application,
} }
}.exhaustive }.exhaustive
} }
}
} }
sealed class Operation { sealed class Operation {
...@@ -579,6 +589,7 @@ sealed class Operation { ...@@ -579,6 +589,7 @@ sealed class Operation {
val toUpdate: List<ChatRoomEntity>, val toUpdate: List<ChatRoomEntity>,
val toRemove: List<String> val toRemove: List<String>
) : Operation() ) : Operation()
data class InsertRooms(val chatRooms: List<ChatRoomEntity>) : Operation() data class InsertRooms(val chatRooms: List<ChatRoomEntity>) : Operation()
data class CleanInsertRooms(val chatRooms: List<ChatRoomEntity>) : Operation() data class CleanInsertRooms(val chatRooms: List<ChatRoomEntity>) : Operation()
...@@ -609,7 +620,7 @@ private fun String.databaseName(): String { ...@@ -609,7 +620,7 @@ private fun String.databaseName(): String {
val tmp = this.removePrefix("https://") val tmp = this.removePrefix("https://")
.removePrefix("http://") .removePrefix("http://")
.removeTrailingSlash() .removeTrailingSlash()
.replace("/","-") .replace("/", "-")
.replace(".", "_") .replace(".", "_")
return "$tmp.db" return "$tmp.db"
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.db ...@@ -2,6 +2,7 @@ package chat.rocket.android.db
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import chat.rocket.android.db.model.AttachmentActionEntity import chat.rocket.android.db.model.AttachmentActionEntity
import chat.rocket.android.db.model.AttachmentEntity import chat.rocket.android.db.model.AttachmentEntity
import chat.rocket.android.db.model.AttachmentFieldEntity import chat.rocket.android.db.model.AttachmentFieldEntity
...@@ -14,6 +15,7 @@ import chat.rocket.android.db.model.MessagesSync ...@@ -14,6 +15,7 @@ import chat.rocket.android.db.model.MessagesSync
import chat.rocket.android.db.model.ReactionEntity import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.UrlEntity import chat.rocket.android.db.model.UrlEntity
import chat.rocket.android.db.model.UserEntity import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.emoji.internal.db.StringListConverter
@Database( @Database(
entities = [ entities = [
...@@ -23,9 +25,10 @@ import chat.rocket.android.db.model.UserEntity ...@@ -23,9 +25,10 @@ import chat.rocket.android.db.model.UserEntity
AttachmentFieldEntity::class, AttachmentActionEntity::class, UrlEntity::class, AttachmentFieldEntity::class, AttachmentActionEntity::class, UrlEntity::class,
ReactionEntity::class, MessagesSync::class ReactionEntity::class, MessagesSync::class
], ],
version = 10, version = 12,
exportSchema = true exportSchema = true
) )
@TypeConverters(StringListConverter::class)
abstract class RCDatabase : RoomDatabase() { abstract class RCDatabase : RoomDatabase() {
abstract fun userDao(): UserDao abstract fun userDao(): UserDao
abstract fun chatRoomDao(): ChatRoomDao abstract fun chatRoomDao(): ChatRoomDao
......
...@@ -5,6 +5,8 @@ import androidx.room.Entity ...@@ -5,6 +5,8 @@ import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import chat.rocket.android.emoji.internal.db.StringListConverter
@Entity(tableName = "chatrooms", @Entity(tableName = "chatrooms",
indices = [ indices = [
...@@ -20,6 +22,7 @@ import androidx.room.PrimaryKey ...@@ -20,6 +22,7 @@ import androidx.room.PrimaryKey
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["lastMessageUserId"]) ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["lastMessageUserId"])
] ]
) )
@TypeConverters(StringListConverter::class)
data class ChatRoomEntity( data class ChatRoomEntity(
@PrimaryKey var id: String, @PrimaryKey var id: String,
var subscriptionId: String, var subscriptionId: String,
...@@ -42,7 +45,8 @@ data class ChatRoomEntity( ...@@ -42,7 +45,8 @@ data class ChatRoomEntity(
var lastMessageText: String? = null, var lastMessageText: String? = null,
var lastMessageUserId: String? = null, var lastMessageUserId: String? = null,
var lastMessageTimestamp: Long? = null, var lastMessageTimestamp: Long? = null,
var broadcast: Boolean? = false var broadcast: Boolean? = false,
var muted: List<String>? = null
) )
data class ChatRoom( data class ChatRoom(
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
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
......
...@@ -7,6 +7,7 @@ import chat.rocket.android.files.uimodel.FileUiModel ...@@ -7,6 +7,7 @@ import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.files.uimodel.FileUiModelMapper import chat.rocket.android.files.uimodel.FileUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
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
......
...@@ -6,6 +6,7 @@ import chat.rocket.android.members.uimodel.MemberUiModel ...@@ -6,6 +6,7 @@ import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.members.uimodel.MemberUiModelMapper import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
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
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
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
......
...@@ -296,11 +296,8 @@ class PushManager @Inject constructor( ...@@ -296,11 +296,8 @@ class PushManager @Inject constructor(
} }
private fun getContentIntent(context: Context, notificationId: Int, pushMessage: PushMessage, grouped: Boolean = false): PendingIntent { private fun getContentIntent(context: Context, notificationId: Int, pushMessage: PushMessage, grouped: Boolean = false): PendingIntent {
val notificationIntent = context.changeServerIntent(pushMessage.info.host, chatRoomId = pushMessage.info.roomId) val roomId = if (!grouped) pushMessage.info.roomId else null
// TODO - add support to go directly to the chatroom val notificationIntent = context.changeServerIntent(pushMessage.info.host, chatRoomId = roomId)
/*if (!isGrouped) {
notificationIntent.putExtra(EXTRA_ROOM_ID, pushMessage.info.roomId)
}*/
return PendingIntent.getActivity(context, random.nextInt(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getActivity(context, random.nextInt(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
} }
......
...@@ -7,6 +7,7 @@ import chat.rocket.android.db.model.FullMessage ...@@ -7,6 +7,7 @@ import chat.rocket.android.db.model.FullMessage
import chat.rocket.android.db.model.ReactionEntity import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.UrlEntity import chat.rocket.android.db.model.UrlEntity
import chat.rocket.android.db.model.UserEntity import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.util.retryDB
import chat.rocket.common.model.SimpleRoom import chat.rocket.common.model.SimpleRoom
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
...@@ -135,14 +136,18 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -135,14 +136,18 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
with(attachment) { with(attachment) {
val fields = if (hasFields) { val fields = if (hasFields) {
withContext(CommonPool) { withContext(CommonPool) {
retryDB("getAttachmentFields(${attachment._id})") {
dbManager.messageDao().getAttachmentFields(attachment._id) dbManager.messageDao().getAttachmentFields(attachment._id)
}
}.map { Field(it.title, it.value) } }.map { Field(it.title, it.value) }
} else { } else {
null null
} }
val actions = if (hasActions) { val actions = if (hasActions) {
withContext(CommonPool) { withContext(CommonPool) {
retryDB("getAttachmentActions(${attachment._id})") {
dbManager.messageDao().getAttachmentActions(attachment._id) dbManager.messageDao().getAttachmentActions(attachment._id)
}
}.mapNotNull { mapAction(it) } }.mapNotNull { mapAction(it) }
} else { } else {
null null
...@@ -183,29 +188,6 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -183,29 +188,6 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
return list return list
} }
/*private suspend fun mapColorAttachmentWithFields(entity: AttachmentEntity): ColorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(entity._id)
}.map { Field(it.title, it.value) }
return with(entity) {
ColorAttachment(
color = Color.Custom(color ?: DEFAULT_COLOR_STR),
text = text ?: "",
fallback = fallback,
fields = fields)
}
}
private suspend fun mapActionAttachment(attachment: AttachmentEntity): ActionsAttachment {
val actions = withContext(CommonPool) {
dbManager.messageDao().getAttachmentActions(attachment._id)
}.mapNotNull { mapAction(it) }
return with(attachment) {
// TODO - remove the default "vertical" value from here...
ActionsAttachment(title, actions, buttonAlignment ?: "vertical")
}
}*/
private fun mapAction(action: AttachmentActionEntity): Action? { private fun mapAction(action: AttachmentActionEntity): Action? {
return when (action.type) { return when (action.type) {
"button" -> ButtonAction(action.type, action.text, action.url, action.isWebView, "button" -> ButtonAction(action.type, action.text, action.url, action.isWebView,
...@@ -214,13 +196,4 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -214,13 +196,4 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
else -> null else -> null
} }
} }
/*private suspend fun mapAuthorAttachment(attachment: AttachmentEntity): AuthorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(attachment._id)
}.map { Field(it.title, it.value) }
return with(attachment) {
AuthorAttachment(authorLink!!, authorIcon, authorName, fields)
}
}*/
} }
\ No newline at end of file
...@@ -4,6 +4,7 @@ import chat.rocket.android.db.DatabaseManager ...@@ -4,6 +4,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.Operation import chat.rocket.android.db.Operation
import chat.rocket.android.db.model.MessagesSync import chat.rocket.android.db.model.MessagesSync
import chat.rocket.android.server.domain.MessagesRepository import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.util.retryDB
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
...@@ -14,26 +15,32 @@ class DatabaseMessagesRepository( ...@@ -14,26 +15,32 @@ class DatabaseMessagesRepository(
) : MessagesRepository { ) : MessagesRepository {
override suspend fun getById(id: String): Message? = withContext(CommonPool) { override suspend fun getById(id: String): Message? = withContext(CommonPool) {
retryDB("getMessageById($id)") {
dbManager.messageDao().getMessageById(id)?.let { message -> mapper.map(message) } dbManager.messageDao().getMessageById(id)?.let { message -> mapper.map(message) }
} }
}
override suspend fun getByRoomId(roomId: String): List<Message> = withContext(CommonPool) { override suspend fun getByRoomId(roomId: String): List<Message> = withContext(CommonPool) {
// FIXME - investigate how to avoid this distinctBy here, since DAO is returning a lot of // FIXME - investigate how to avoid this distinctBy here, since DAO is returning a lot of
// duplicate rows (something related to our JOINS and relations on Room) // duplicate rows (something related to our JOINS and relations on Room)
retryDB("getMessagesByRoomId($roomId)") {
dbManager.messageDao().getMessagesByRoomId(roomId) dbManager.messageDao().getMessagesByRoomId(roomId)
.distinctBy { it.message.message.id } .distinctBy { it.message.message.id }
.let { messages -> .let { messages ->
mapper.map(messages) mapper.map(messages)
} }
} }
}
override suspend fun getRecentMessages(roomId: String, count: Long): List<Message> = withContext(CommonPool) { override suspend fun getRecentMessages(roomId: String, count: Long): List<Message> = withContext(CommonPool) {
retryDB("getRecentMessagesByRoomId($roomId, $count)") {
dbManager.messageDao().getRecentMessagesByRoomId(roomId, count) dbManager.messageDao().getRecentMessagesByRoomId(roomId, count)
.distinctBy { it.message.message.id } .distinctBy { it.message.message.id }
.let { messages -> .let { messages ->
mapper.map(messages) mapper.map(messages)
} }
} }
}
override suspend fun save(message: Message) { override suspend fun save(message: Message) {
dbManager.processMessagesBatch(listOf(message)).join() dbManager.processMessagesBatch(listOf(message)).join()
...@@ -45,27 +52,33 @@ class DatabaseMessagesRepository( ...@@ -45,27 +52,33 @@ class DatabaseMessagesRepository(
override suspend fun removeById(id: String) { override suspend fun removeById(id: String) {
withContext(CommonPool) { withContext(CommonPool) {
dbManager.messageDao().delete(id) retryDB("delete($id)") { dbManager.messageDao().delete(id) }
} }
} }
override suspend fun removeByRoomId(roomId: String) { override suspend fun removeByRoomId(roomId: String) {
withContext(CommonPool) { withContext(CommonPool) {
retryDB("deleteByRoomId($roomId)") {
dbManager.messageDao().deleteByRoomId(roomId) dbManager.messageDao().deleteByRoomId(roomId)
} }
} }
}
override suspend fun getAllUnsent(): List<Message> = withContext(CommonPool) { override suspend fun getAllUnsent(): List<Message> = withContext(CommonPool) {
retryDB("getUnsentMessages") {
dbManager.messageDao().getUnsentMessages() dbManager.messageDao().getUnsentMessages()
.distinctBy { it.message.message.id } .distinctBy { it.message.message.id }
.let { mapper.map(it) } .let { mapper.map(it) }
} }
}
override suspend fun saveLastSyncDate(roomId: String, timeMillis: Long) { override suspend fun saveLastSyncDate(roomId: String, timeMillis: Long) {
dbManager.sendOperation(Operation.SaveLastSync(MessagesSync(roomId, timeMillis))) dbManager.sendOperation(Operation.SaveLastSync(MessagesSync(roomId, timeMillis)))
} }
override suspend fun getLastSyncDate(roomId: String): Long? = withContext(CommonPool) { override suspend fun getLastSyncDate(roomId: String): Long? = withContext(CommonPool) {
retryDB("getLastSync($roomId)") {
dbManager.messageDao().getLastSync(roomId)?.let { it.timestamp } dbManager.messageDao().getLastSync(roomId)?.let { it.timestamp }
} }
}
} }
\ No newline at end of file
package chat.rocket.android.settings.password.presentation package chat.rocket.android.settings.password.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.core.lifecycle.CancelStrategy 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
...@@ -14,6 +15,7 @@ import javax.inject.Inject ...@@ -14,6 +15,7 @@ import javax.inject.Inject
class PasswordPresenter @Inject constructor( class PasswordPresenter @Inject constructor(
private val view: PasswordView, private val view: PasswordView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val analyticsManager: AnalyticsManager,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory factory: RocketChatClientFactory
) { ) {
...@@ -30,10 +32,12 @@ class PasswordPresenter @Inject constructor( ...@@ -30,10 +32,12 @@ class PasswordPresenter @Inject constructor(
client.updateProfile(me.id, null, null, password, null) client.updateProfile(me.id, null, null, password, null)
} }
analyticsManager.logResetPassword(true)
view.showPasswordSuccessfullyUpdatedMessage() view.showPasswordSuccessfullyUpdatedMessage()
view.hideLoading()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
analyticsManager.logResetPassword(false)
view.showPasswordFailsUpdateMessage(exception.message) view.showPasswordFailsUpdateMessage(exception.message)
} finally {
view.hideLoading() view.hideLoading()
} }
} }
......
package chat.rocket.android.settings.ui package chat.rocket.android.settings.ui
import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.about.ui.AboutFragment import chat.rocket.android.about.ui.AboutFragment
...@@ -84,7 +87,15 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen ...@@ -84,7 +87,15 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
shareIntent.putExtra(Intent.EXTRA_TEXT, shareSub) shareIntent.putExtra(Intent.EXTRA_TEXT, shareSub)
startActivity(Intent.createChooser(shareIntent, getString(R.string.msg_share_using))) startActivity(Intent.createChooser(shareIntent, getString(R.string.msg_share_using)))
} }
resources.getString(R.string.title_rate_us) -> startAppPlayStore()
}
}
private fun startAppPlayStore() {
try {
startActivity(Intent(Intent.ACTION_VIEW, getString(R.string.market_link).toUri()))
} catch (error: ActivityNotFoundException) {
startActivity(Intent(Intent.ACTION_VIEW, getString(R.string.play_store_link).toUri()))
} }
} }
......
package chat.rocket.android.util package chat.rocket.android.util
import android.database.sqlite.SQLiteDatabaseLockedException
import chat.rocket.common.RocketChatNetworkErrorException import chat.rocket.common.RocketChatNetworkErrorException
import kotlinx.coroutines.experimental.TimeoutCancellationException import kotlinx.coroutines.experimental.TimeoutCancellationException
import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.delay
...@@ -8,6 +9,7 @@ import timber.log.Timber ...@@ -8,6 +9,7 @@ import timber.log.Timber
import kotlin.coroutines.experimental.coroutineContext import kotlin.coroutines.experimental.coroutineContext
const val DEFAULT_RETRY = 3 const val DEFAULT_RETRY = 3
private const val DEFAULT_DB_RETRY = 5
suspend fun <T> retryIO( suspend fun <T> retryIO(
description: String = "<missing description>", description: String = "<missing description>",
...@@ -35,3 +37,30 @@ suspend fun <T> retryIO( ...@@ -35,3 +37,30 @@ suspend fun <T> retryIO(
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled") if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
return block() // last attempt return block() // last attempt
} }
suspend fun <T> retryDB(
description: String = "<missing description>",
times: Int = DEFAULT_DB_RETRY,
initialDelay: Long = 100, // 0.1 second
maxDelay: Long = 500, // 0.5 second
factor: Double = 1.2,
block: suspend () -> T): T
{
var currentDelay = initialDelay
repeat(times - 1) { currentTry ->
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
try {
return block()
} catch (e: SQLiteDatabaseLockedException) {
Timber.d(e, "failed call($currentTry): $description")
e.printStackTrace()
}
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
}
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
return block() // last attempt
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center">
<ImageView
android:id="@+id/emoji_image_view"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
tools:src="@tools:sample/avatars" />
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/emoji_view"
android:layout_width="48dp"
android:layout_height="48dp"
android:foreground="?selectableItemBackground"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#000000"
android:textSize="24sp"
tools:text="😀" />
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorWhite"
android:padding="16dp">
<ViewFlipper
android:id="@+id/view_flipper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foregroundGravity="center"
app:layout_constraintTop_toTopOf="parent">
<include layout="@layout/emoji_row_item" />
<include layout="@layout/emoji_image_row_item" />
</ViewFlipper>
<TextView
android:id="@+id/text_view_usernames"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textColor="@color/darkGray"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@+id/view_flipper"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/view_flipper"
app:layout_constraintTop_toTopOf="@+id/view_flipper"
tools:text="Ann reacted with :grin:" />
</androidx.constraintlayout.widget.ConstraintLayout>
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">Einstellungen</string> <string name="title_settings">Einstellungen</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation --> <string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">Ändere Passwort</string> <string name="title_change_password">Ändere Passwort</string>
<string name="title_rate_us">Bewerten Sie uns</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation --> <string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Ändere Passwort</string> <string name="title_password">Ändere Passwort</string>
<string name="title_update_profile">Update Profil</string> <string name="title_update_profile">Update Profil</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation --> <item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Ändere Passwort</item> <item name="item_password">Ändere Passwort</item>
<item name="item_password">Über</item>
<item name="item_share_app">App teilen</item> <item name="item_share_app">App teilen</item>
<item name="item_rate_us">Bewerten Sie uns</item>
<item name="item_password">Über</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -148,6 +150,13 @@ ...@@ -148,6 +150,13 @@
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation --> <string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation --> <string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Privat</string> <string name="msg_private_channel">Privat</string>
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<string name="title_settings">Configuraciones</string> <string name="title_settings">Configuraciones</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation --> <string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">Cambia la contraseña</string> <string name="title_change_password">Cambia la contraseña</string>
<string name="title_rate_us">Nos califica</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation --> <string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Cambia la contraseña</string> <string name="title_password">Cambia la contraseña</string>
<string name="title_update_profile">Actualización del perfil</string> <string name="title_update_profile">Actualización del perfil</string>
...@@ -57,8 +58,9 @@ ...@@ -57,8 +58,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation --> <item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Cambia la contraseña</item> <item name="item_password">Cambia la contraseña</item>
<item name="item_password">Acerca de</item>
<item name="item_share_app">Compartir aplicación</item> <item name="item_share_app">Compartir aplicación</item>
<item name="item_rate_us">Nos califica</item>
<item name="item_password">Acerca de</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -166,6 +168,13 @@ ...@@ -166,6 +168,13 @@
<string name="msg_view_less">view less</string> <string name="msg_view_less">view less</string>
<!-- TODO - Add proper translation --> <!-- TODO - Add proper translation -->
<string name="msg_permalink_copied">Permalink copied</string> <string name="msg_permalink_copied">Permalink copied</string>
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Preferences messages --> <!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string> <!-- TODO Add translation --> <string name="msg_analytics_tracking">Analytics tracking</string> <!-- TODO Add translation -->
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">Paramètres</string> <string name="title_settings">Paramètres</string>
<string name="title_preferences">Préférences</string> <string name="title_preferences">Préférences</string>
<string name="title_change_password">Changer le mot de passe</string> <string name="title_change_password">Changer le mot de passe</string>
<string name="title_rate_us">évaluez nous</string>
<string name="title_admin_panel">Administration</string> <string name="title_admin_panel">Administration</string>
<string name="title_password">Changer le mot de passe</string> <string name="title_password">Changer le mot de passe</string>
<string name="title_update_profile">Mettre à jour le profil</string> <string name="title_update_profile">Mettre à jour le profil</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Préférences</item> <item name="item_preferences">Préférences</item>
<item name="item_password">Changer le mot de passe</item> <item name="item_password">Changer le mot de passe</item>
<item name="item_password">À propos</item>
<item name="item_share_app">Partager l\'application</item> <item name="item_share_app">Partager l\'application</item>
<item name="item_rate_us">évaluez nous</item>
<item name="item_password">À propos</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -158,6 +160,13 @@ ...@@ -158,6 +160,13 @@
<string name="msg_view_less">view less</string> <string name="msg_view_less">view less</string>
<!-- TODO - Add proper translation --> <!-- TODO - Add proper translation -->
<string name="msg_permalink_copied">Permalink copied</string> <string name="msg_permalink_copied">Permalink copied</string>
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Privé</string> <string name="msg_private_channel">Privé</string>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">सेटिंग्स</string> <string name="title_settings">सेटिंग्स</string>
<string name="title_preferences">प्राथमिकताएँ</string> <string name="title_preferences">प्राथमिकताएँ</string>
<string name="title_change_password">पासवर्ड बदलें</string> <string name="title_change_password">पासवर्ड बदलें</string>
<string name="title_rate_us">हमें रेटिंग दें</string>
<string name="title_admin_panel">एडमिन पैनल</string> <string name="title_admin_panel">एडमिन पैनल</string>
<string name="title_password">पासवर्ड बदलें</string> <string name="title_password">पासवर्ड बदलें</string>
<string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string> <string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">प्राथमिकताएँ</item> <item name="item_preferences">प्राथमिकताएँ</item>
<item name="item_password">पासवर्ड बदलें</item> <item name="item_password">पासवर्ड बदलें</item>
<item name="item_password">परिचय</item>
<item name="item_share_app">ऐप शेयर करें</item> <item name="item_share_app">ऐप शेयर करें</item>
<item name="item_rate_us">हमें रेटिंग दें</item>
<item name="item_password">परिचय</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -170,6 +172,13 @@ ...@@ -170,6 +172,13 @@
<string name="msg_view_less">कम देखें</string> <string name="msg_view_less">कम देखें</string>
<!-- TODO - Add proper translation --> <!-- TODO - Add proper translation -->
<string name="msg_permalink_copied">Permalink copied</string> <string name="msg_permalink_copied">Permalink copied</string>
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Preferences messages --> <!-- Preferences messages -->
<string name="msg_analytics_tracking">एनालिटिक्स ट्रैकिंग</string> <string name="msg_analytics_tracking">एनालिटिक्स ट्रैकिंग</string>
......
This diff is collapsed.
...@@ -18,11 +18,13 @@ ...@@ -18,11 +18,13 @@
<string name="title_settings">設定</string> <string name="title_settings">設定</string>
<string name="title_preferences">環境設定</string> <string name="title_preferences">環境設定</string>
<string name="title_change_password">パスワードの変更</string> <string name="title_change_password">パスワードの変更</string>
<string name="title_rate_us">私たちを評価してください</string>
<string name="title_admin_panel">管理パネル</string> <string name="title_admin_panel">管理パネル</string>
<string name="title_password">パスワードの変更</string> <string name="title_password">パスワードの変更</string>
<string name="title_update_profile">プロフィールの更新</string> <string name="title_update_profile">プロフィールの更新</string>
<string name="title_about">About</string> <string name="title_about">About</string>
<string name="title_create_channel">新しいチャネルを作成</string> <string name="title_create_channel">新しいチャネルを作成</string>
<string name="title_are_you_sure">本気ですか?</string>
<!-- Actions --> <!-- Actions -->
<string name="action_connect">接続</string> <string name="action_connect">接続</string>
...@@ -53,14 +55,15 @@ ...@@ -53,14 +55,15 @@
<string name="action_create_server">新規サーバーを作成</string> <string name="action_create_server">新規サーバーを作成</string>
<string name="action_register">登録</string> <string name="action_register">登録</string>
<string name="action_confirm">確認</string> <string name="action_confirm">確認</string>
<string name="action_delete_account">Delete account</string> <!-- TODO Add translation --> <string name="action_delete_account">アカウントを削除する</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">環境設定</item> <item name="item_preferences">環境設定</item>
<item name="item_password">パスワードの変更</item> <item name="item_password">パスワードの変更</item>
<item name="item_password">アプリ情報</item>
<item name="item_share_app">アプリを共有する</item> <item name="item_share_app">アプリを共有する</item>
<item name="item_rate_us">私たちを評価してください</item>
<item name="item_password">アプリ情報</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -152,10 +155,14 @@ ...@@ -152,10 +155,14 @@
<string name="msg_continue_with_wordpress"><b>WordPress</b>でログイン</string> <string name="msg_continue_with_wordpress"><b>WordPress</b>でログイン</string>
<string name="msg_two_factor_authentication">二要素認証</string> <string name="msg_two_factor_authentication">二要素認証</string>
<string name="msg__your_2fa_code">あなたの 2FA コードは何ですか?</string> <string name="msg__your_2fa_code">あなたの 2FA コードは何ですか?</string>
<string name="msg_view_more">更に表示</string> <string name="msg_view_more">更に表示</string>
<string name="msg_view_less">隠す</string> <string name="msg_view_less">隠す</string>
<string name="msg_muted_on_this_channel">あなたはこのチャンネルでミュートされています</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">プライベート</string> <string name="msg_private_channel">プライベート</string>
...@@ -209,7 +216,6 @@ ...@@ -209,7 +216,6 @@
<string name="action_msg_share">Share</string> <string name="action_msg_share">Share</string>
<string name="action_title_editing">メッセージの編集</string> <string name="action_title_editing">メッセージの編集</string>
<string name="action_msg_add_reaction">リアクションする</string> <string name="action_msg_add_reaction">リアクションする</string>
<!-- TODO - Add proper translation -->
<string name="action_msg_copy_permalink">パーマリンクのコピー</string> <string name="action_msg_copy_permalink">パーマリンクのコピー</string>
<!-- Permission messages --> <!-- Permission messages -->
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">Configurações</string> <string name="title_settings">Configurações</string>
<string name="title_preferences">Preferencias</string> <string name="title_preferences">Preferencias</string>
<string name="title_change_password">Alterar senha</string> <string name="title_change_password">Alterar senha</string>
<string name="title_rate_us">nos avalie</string>
<string name="title_admin_panel">Painel administrativo</string> <string name="title_admin_panel">Painel administrativo</string>
<string name="title_password">Alterar senha</string> <string name="title_password">Alterar senha</string>
<string name="title_update_profile">Editar perfil</string> <string name="title_update_profile">Editar perfil</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferencias</item> <item name="item_preferences">Preferencias</item>
<item name="item_password">Alterar senha</item> <item name="item_password">Alterar senha</item>
<item name="item_password">Sobre</item>
<item name="item_share_app">Compartilhe o aplicativo</item> <item name="item_share_app">Compartilhe o aplicativo</item>
<item name="item_rate_us">nos avalie</item>
<item name="item_password">Sobre</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -157,6 +159,11 @@ ...@@ -157,6 +159,11 @@
<string name="msg_view_less">visualizar menos</string> <string name="msg_view_less">visualizar menos</string>
<!-- TODO - Add proper translation --> <!-- TODO - Add proper translation -->
<string name="msg_permalink_copied">Permalink copiado</string> <string name="msg_permalink_copied">Permalink copiado</string>
<string name="msg_muted_on_this_channel">Você está silenciado neste canal</string>
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reagiu com %2$s</item>
<item quantity="other">%1$s reagiram com %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Privado</string> <string name="msg_private_channel">Privado</string>
...@@ -185,8 +192,8 @@ ...@@ -185,8 +192,8 @@
<string name="message_welcome">Bem-vindo, %s</string> <string name="message_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</string> <string name="message_removed">Mensagem removida</string>
<string name="message_pinned">Pinou uma mensagem:</string> <string name="message_pinned">Pinou uma mensagem:</string>
<string name="message_muted">Usuário %1$s entrou no modo mudo por %2$s</string> <string name="message_muted">Usuário %1$s foi silenciado por %2$s</string>
<string name="message_unmuted">Usuário %1$s saiu do modo mudo por %2$s</string> <string name="message_unmuted">Usuário %1$s saiu do modo silenciado por %2$s</string>
<string name="message_role_add">%1$s foi definido %2$s por %3$s</string> <string name="message_role_add">%1$s foi definido %2$s por %3$s</string>
<string name="message_role_removed">%1$s não é mais %2$s por %3$s</string> <string name="message_role_removed">%1$s não é mais %2$s por %3$s</string>
// TODO:Add proper translation. // TODO:Add proper translation.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">Настройки</string> <string name="title_settings">Настройки</string>
<string name="title_preferences">Персональные</string> <string name="title_preferences">Персональные</string>
<string name="title_change_password">Изменить пароль</string> <string name="title_change_password">Изменить пароль</string>
<string name="title_rate_us">оцените нас</string>
<string name="title_admin_panel">Панель админа</string> <string name="title_admin_panel">Панель админа</string>
<string name="title_password">Изменить пароль</string> <string name="title_password">Изменить пароль</string>
<string name="title_update_profile">Обновить профиль</string> <string name="title_update_profile">Обновить профиль</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Персональные</item> <item name="item_preferences">Персональные</item>
<item name="item_password">Изменить пароль</item> <item name="item_password">Изменить пароль</item>
<item name="item_password">О программе</item> <item name="item_rate_us">оцените нас</item>
<item name="item_share_app">добавить приложение</item> <item name="item_share_app">добавить приложение</item>
<item name="item_password">О программе</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -154,6 +156,14 @@ ...@@ -154,6 +156,14 @@
<string name="msg_view_more">больше</string> <string name="msg_view_more">больше</string>
<string name="msg_view_less">меньше</string> <string name="msg_view_less">меньше</string>
<string name="msg_permalink_copied">Ссылка скопирована</string> <string name="msg_permalink_copied">Ссылка скопирована</string>
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="few">%1$s reacted with %2$s</item>
<item quantity="many">%1$s reacted with %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Приватный</string> <string name="msg_private_channel">Приватный</string>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">Ayarlar</string> <string name="title_settings">Ayarlar</string>
<string name="title_preferences">Tercihler</string> <string name="title_preferences">Tercihler</string>
<string name="title_change_password">Şifre Değişikliği</string> <string name="title_change_password">Şifre Değişikliği</string>
<string name="title_rate_us">Bizi değerlendirin</string>
<string name="title_admin_panel">Yönetici Paneli</string> <string name="title_admin_panel">Yönetici Paneli</string>
<string name="title_password">Şifrenizi Değiştirin</string> <string name="title_password">Şifrenizi Değiştirin</string>
<string name="title_update_profile">Profilinizi Düzenleyin</string> <string name="title_update_profile">Profilinizi Düzenleyin</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Tercihler</item> <item name="item_preferences">Tercihler</item>
<item name="item_password">Şifre Değiştir</item> <item name="item_password">Şifre Değiştir</item>
<item name="item_password">Hakkında</item>
<item name="item_share_app">uygulamayı Paylaş</item> <item name="item_share_app">uygulamayı Paylaş</item>
<item name="item_rate_us">Bizi değerlendirin</item>
<item name="item_password">Hakkında</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -171,6 +173,13 @@ ...@@ -171,6 +173,13 @@
<string name="msg_view_less">Daha az göster</string> <string name="msg_view_less">Daha az göster</string>
<!-- TODO - Add proper translation --> <!-- TODO - Add proper translation -->
<string name="msg_permalink_copied">Permalink copied</string> <string name="msg_permalink_copied">Permalink copied</string>
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Preferences messages --> <!-- Preferences messages -->
<string name="msg_analytics_tracking">İstatistik takibi</string> <string name="msg_analytics_tracking">İstatistik takibi</string>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<string name="title_settings">Налаштування</string> <string name="title_settings">Налаштування</string>
<string name="title_preferences">Персональні</string> <string name="title_preferences">Персональні</string>
<string name="title_change_password">Змінити пароль</string> <string name="title_change_password">Змінити пароль</string>
<string name="title_rate_us">Оцініть нас</string>
<string name="title_admin_panel">Панель адміністратора</string> <string name="title_admin_panel">Панель адміністратора</string>
<string name="title_password">Змінити пароль</string> <string name="title_password">Змінити пароль</string>
<string name="title_update_profile">Оновити профіль</string> <string name="title_update_profile">Оновити профіль</string>
...@@ -58,8 +59,9 @@ ...@@ -58,8 +59,9 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation --> <item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Change Password</item> <!-- TODO Add translation --> <item name="item_password">Change Password</item> <!-- TODO Add translation -->
<item name="item_password">About</item> <!-- TODO Add translation -->
<item name="item_share_app">поділитися прикладом</item> <item name="item_share_app">поділитися прикладом</item>
<item name="item_rate_us">Оцініть нас</item>
<item name="item_password">About</item> <!-- TODO Add translation -->
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -156,6 +158,14 @@ ...@@ -156,6 +158,14 @@
<string name="msg_view_less">view less</string> <string name="msg_view_less">view less</string>
<!-- TODO - Add proper translation --> <!-- TODO - Add proper translation -->
<string name="msg_permalink_copied">Permalink copied</string> <string name="msg_permalink_copied">Permalink copied</string>
<!-- TODO - Add proper translation -->
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- TODO - Add proper translation -->
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="few">%1$s reacted with %2$s</item>
<item quantity="many">%1$s reacted with %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Приватний</string> <string name="msg_private_channel">Приватний</string>
......
...@@ -5,4 +5,5 @@ ...@@ -5,4 +5,5 @@
<string name="community_server_url" translatable="false">open.rocket.chat</string> <string name="community_server_url" translatable="false">open.rocket.chat</string>
<string name="create_server_url" translatable="false">cloud.rocket.chat/trial</string> <string name="create_server_url" translatable="false">cloud.rocket.chat/trial</string>
<string name="play_store_link" translatable="false">https://play.google.com/store/apps/details?id=chat.rocket.android</string> <string name="play_store_link" translatable="false">https://play.google.com/store/apps/details?id=chat.rocket.android</string>
<string name="market_link" translatable="false">market://details?id=chat.rocket.android</string>
</resources> </resources>
\ No newline at end of file
...@@ -28,6 +28,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -28,6 +28,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="title_settings">Settings</string> <string name="title_settings">Settings</string>
<string name="title_preferences">Preferences</string> <string name="title_preferences">Preferences</string>
<string name="title_change_password">Change Password</string> <string name="title_change_password">Change Password</string>
<string name="title_rate_us">Rate Us</string>
<string name="title_admin_panel">Admin panel</string> <string name="title_admin_panel">Admin panel</string>
<string name="title_password">Change Password</string> <string name="title_password">Change Password</string>
<string name="title_update_profile">Update profile</string> <string name="title_update_profile">Update profile</string>
...@@ -70,8 +71,9 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -70,8 +71,9 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <item name="item_preferences">Preferences</item>
<item name="item_password">Change Password</item> <item name="item_password">Change Password</item>
<item name="item_password">About</item>
<item name="item_share_app">Share App</item> <item name="item_share_app">Share App</item>
<item name="item_rate_us">Rate Us</item>
<item name="item_password">About</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -166,6 +168,10 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -166,6 +168,10 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <string name="msg_two_factor_authentication">Two-factor Authentication</string>
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <string name="msg__your_2fa_code">What’s your 2FA code?</string>
<string name="msg_permalink_copied">Permalink copied</string> <string name="msg_permalink_copied">Permalink copied</string>
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item>
</plurals>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Private</string> <string name="msg_private_channel">Private</string>
...@@ -183,6 +189,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -183,6 +189,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="msg_delete_description">Are you sure you want to delete this message</string> <string name="msg_delete_description">Are you sure you want to delete this message</string>
<string name="msg_view_more">view more</string> <string name="msg_view_more">view more</string>
<string name="msg_view_less">view less</string> <string name="msg_view_less">view less</string>
<string name="msg_muted_on_this_channel">You are muted on this channel</string>
<!-- Preferences messages --> <!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string> <string name="msg_analytics_tracking">Analytics tracking</string>
......
...@@ -67,4 +67,11 @@ class AnswersAnalytics : Analytics { ...@@ -67,4 +67,11 @@ class AnswersAnalytics : Analytics {
) )
override fun logOpenAdmin() = Answers.getInstance().logCustom(CustomEvent("open_admin")) override fun logOpenAdmin() = Answers.getInstance().logCustom(CustomEvent("open_admin"))
override fun logResetPassword(resetPasswordSucceeded: Boolean) =
Answers.getInstance()
.logCustom(
CustomEvent("reset_password")
.putCustomAttribute("resetPasswordSucceeded", resetPasswordSucceeded.toString())
)
} }
...@@ -60,4 +60,9 @@ class GoogleAnalyticsForFirebase @Inject constructor(val context: Context) : ...@@ -60,4 +60,9 @@ class GoogleAnalyticsForFirebase @Inject constructor(val context: Context) :
} }
override fun logOpenAdmin() = firebaseAnalytics.logEvent("open_admin", null) override fun logOpenAdmin() = firebaseAnalytics.logEvent("open_admin", null)
override fun logResetPassword(resetPasswordSucceeded: Boolean) =
firebaseAnalytics.logEvent("reset_password", Bundle(1).apply {
putBoolean("resetPasswordSucceeded", resetPasswordSucceeded)
})
} }
package chat.rocket.android.push package chat.rocket.android.push
import android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.NetworkType import androidx.work.NetworkType
...@@ -23,9 +24,12 @@ class FirebaseMessagingService : FirebaseMessagingService() { ...@@ -23,9 +24,12 @@ class FirebaseMessagingService : FirebaseMessagingService() {
} }
override fun onMessageReceived(message: RemoteMessage) { override fun onMessageReceived(message: RemoteMessage) {
// XXX - for now this is ok, if we start to do network calls, use a Worker instead message.data?.let { data ->
message.data?.let { val bundle = Bundle()
pushManager.handle(bundleOf(*(it.map { Pair(it.key, it.value) }).toTypedArray())) data.entries.forEach { entry ->
bundle.putString(entry.key, entry.value)
}
pushManager.handle(bundle)
} }
} }
......
...@@ -16,4 +16,14 @@ interface EmojiReactionListener { ...@@ -16,4 +16,14 @@ interface EmojiReactionListener {
* @param emojiShortname The shortname of the emoji (:grin:, :smiley:, etc). * @param emojiShortname The shortname of the emoji (:grin:, :smiley:, etc).
*/ */
fun onReactionTouched(messageId: String, emojiShortname: String) fun onReactionTouched(messageId: String, emojiShortname: String)
/**
* Callback when an added reaction is long-clicked.
*
* @param shortname The shortname of the emoji (:grin:, :smiley:, etc).
* @param isCustom Whether the reaction is custom or one of the defaults.
* @param url In case of a custom emoji, this is the url to find it. Can be null if not a custom.
* @param usernames The list of usernames of users who added the reaction.
*/
fun onReactionLongClicked(shortname: String, isCustom: Boolean, url: String?, usernames: List<String>)
} }
<resources> <resources>
<string name="msg_no_recent_emoji">No recent emoji</string> <string name="msg_no_recent_emoji">No recent emoji</string>
<string name="alert_title_default_skin_tone">Default skin tone</string> <string name="alert_title_default_skin_tone">Default skin tone</string>
<string name="msg_reactions" translatable="false">Reactions</string>
</resources> </resources>
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