Commit 638759d7 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Merge branch 'beta' into develop

parents 18afd21c c3d08b98
...@@ -12,8 +12,8 @@ android { ...@@ -12,8 +12,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion versions.minSdk minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2030 versionCode 2031
versionName "2.4.0" versionName "2.5.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
......
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "e389d26bfb975f00c75dc6fc5d06d012",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `username` TEXT, `name` TEXT, `status` TEXT NOT NULL, `utcOffset` REAL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "utcOffset",
"columnName": "utcOffset",
"affinity": "REAL",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_users_username",
"unique": false,
"columnNames": [
"username"
],
"createSql": "CREATE INDEX `index_users_username` ON `${TABLE_NAME}` (`username`)"
}
],
"foreignKeys": []
},
{
"tableName": "chatrooms",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, `broadcast` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subscriptionId",
"columnName": "subscriptionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fullname",
"columnName": "fullname",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ownerId",
"columnName": "ownerId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "readonly",
"columnName": "readonly",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isDefault",
"columnName": "isDefault",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "favorite",
"columnName": "favorite",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "open",
"columnName": "open",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "alert",
"columnName": "alert",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userMentions",
"columnName": "userMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "groupMentions",
"columnName": "groupMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "updatedAt",
"columnName": "updatedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeen",
"columnName": "lastSeen",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastMessageText",
"columnName": "lastMessageText",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageUserId",
"columnName": "lastMessageUserId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageTimestamp",
"columnName": "lastMessageTimestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "broadcast",
"columnName": "broadcast",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_chatrooms_userId",
"unique": false,
"columnNames": [
"userId"
],
"createSql": "CREATE INDEX `index_chatrooms_userId` ON `${TABLE_NAME}` (`userId`)"
},
{
"name": "index_chatrooms_ownerId",
"unique": false,
"columnNames": [
"ownerId"
],
"createSql": "CREATE INDEX `index_chatrooms_ownerId` ON `${TABLE_NAME}` (`ownerId`)"
},
{
"name": "index_chatrooms_subscriptionId",
"unique": true,
"columnNames": [
"subscriptionId"
],
"createSql": "CREATE UNIQUE INDEX `index_chatrooms_subscriptionId` ON `${TABLE_NAME}` (`subscriptionId`)"
},
{
"name": "index_chatrooms_updatedAt",
"unique": false,
"columnNames": [
"updatedAt"
],
"createSql": "CREATE INDEX `index_chatrooms_updatedAt` ON `${TABLE_NAME}` (`updatedAt`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"ownerId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"lastMessageUserId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"e389d26bfb975f00c75dc6fc5d06d012\")"
]
}
}
\ No newline at end of file
...@@ -76,7 +76,12 @@ ...@@ -76,7 +76,12 @@
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment --> <activity
android:name=".chatinformation.ui.MessageInfoActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity <activity
android:name=".settings.password.ui.PasswordActivity" android:name=".settings.password.ui.PasswordActivity"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
......
...@@ -5,19 +5,58 @@ import chat.rocket.android.authentication.presentation.AuthenticationNavigator ...@@ -5,19 +5,58 @@ 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.OauthHelper import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.casLoginUrl
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.gitlabUrl
import chat.rocket.android.server.domain.isCasAuthenticationEnabled
import chat.rocket.android.server.domain.isFacebookAuthenticationEnabled
import chat.rocket.android.server.domain.isGithubAuthenticationEnabled
import chat.rocket.android.server.domain.isGitlabAuthenticationEnabled
import chat.rocket.android.server.domain.isGoogleAuthenticationEnabled
import chat.rocket.android.server.domain.isLdapAuthenticationEnabled
import chat.rocket.android.server.domain.isLinkedinAuthenticationEnabled
import chat.rocket.android.server.domain.isLoginFormEnabled
import chat.rocket.android.server.domain.isMeteorAuthenticationEnabled
import chat.rocket.android.server.domain.isPasswordResetEnabled
import chat.rocket.android.server.domain.isRegistrationEnabledForNewUsers
import chat.rocket.android.server.domain.isTwitterAuthenticationEnabled
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
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.extensions.* import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.casUrl
import chat.rocket.android.util.extensions.encodeToBase64
import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extensions.parseColor
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.samlUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO 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.RocketChatTwoFactorException import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.model.Email
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.common.model.User
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.login
import chat.rocket.core.internal.rest.loginWithCas
import chat.rocket.core.internal.rest.loginWithEmail
import chat.rocket.core.internal.rest.loginWithLdap
import chat.rocket.core.internal.rest.loginWithOauth
import chat.rocket.core.internal.rest.loginWithSaml
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.settingsOauth
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
...@@ -351,11 +390,20 @@ class LoginPresenter @Inject constructor( ...@@ -351,11 +390,20 @@ class LoginPresenter @Inject constructor(
} }
} }
} }
val username = retryIO("me()") { client.me().username } val myself = retryIO("me()") { client.me() }
if (username != null) { if (myself.username != null) {
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username) val user = User(
id = myself.id,
roles = myself.roles,
status = myself.status,
name = myself.name,
emails = myself.emails?.map { Email(it.address ?: "", it.verified) },
username = myself.username,
utcOffset = myself.utcOffset
)
localRepository.saveCurrentUser(url = currentServer, user = user)
saveCurrentServer.save(currentServer) saveCurrentServer.save(currentServer)
saveAccount(username) saveAccount(myself.username!!)
saveToken(token) saveToken(token)
registerPushToken() registerPushToken()
if (loginType == TYPE_LOGIN_USER_EMAIL) { if (loginType == TYPE_LOGIN_USER_EMAIL) {
...@@ -391,7 +439,7 @@ class LoginPresenter @Inject constructor( ...@@ -391,7 +439,7 @@ class LoginPresenter @Inject constructor(
}.toString() }.toString()
} }
private fun getSamlServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> { private fun getSamlServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> {
return listMap.filter { map -> map["service"] == "saml" } return listMap.filter { map -> map["service"] == "saml" }
} }
......
package chat.rocket.android.chatinformation.adapter
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter.ReadReceiptViewHolder
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel
import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_read_receipt.view.*
class ReadReceiptAdapter : RecyclerView.Adapter<ReadReceiptViewHolder>() {
private val data = ArrayList<ReadReceiptViewModel>()
init {
setHasStableIds(true)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReadReceiptViewHolder {
return ReadReceiptViewHolder(parent.inflate(R.layout.item_read_receipt, false))
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: ReadReceiptViewHolder, position: Int) {
holder.bind(data[position])
}
fun addAll(items: List<ReadReceiptViewModel>) {
data.clear()
data.addAll(items)
notifyItemRangeInserted(0, items.size)
}
class ReadReceiptViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(readReceipt: ReadReceiptViewModel) {
with(itemView) {
image_avatar.setImageURI(readReceipt.avatar)
receipt_name.text = readReceipt.name
receipt_time.text = readReceipt.time
}
}
}
}
package chat.rocket.android.chatinformation.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatinformation.presentation.MessageInfoView
import chat.rocket.android.chatinformation.ui.MessageInfoFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class MessageInfoFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun messageInfoView(frag: MessageInfoFragment): MessageInfoView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: MessageInfoFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
package chat.rocket.android.chatinformation.di
import chat.rocket.android.chatinformation.ui.MessageInfoFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class MessageInfoFragmentProvider {
@ContributesAndroidInjector(modules = [MessageInfoFragmentModule::class])
@PerFragment
abstract fun provideMessageInfoFragment(): MessageInfoFragment
}
package chat.rocket.android.chatinformation.presentation
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.getMessageReadReceipts
import timber.log.Timber
import javax.inject.Inject
class MessageInfoPresenter @Inject constructor(
private val view: MessageInfoView,
private val strategy: CancelStrategy,
private val mapper: UiModelMapper,
serverInteractor: GetCurrentServerInteractor,
factory: ConnectionManagerFactory
) {
private val currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer)
private val client = manager.client
fun loadReadReceipts(messageId: String) {
launchUI(strategy) {
try {
view.showLoading()
val readReceipts = retryIO(description = "getMessageReadReceipts") {
client.getMessageReadReceipts(messageId = messageId).result
}
view.showReadReceipts(mapper.map(readReceipts))
} catch (ex: RocketChatException) {
Timber.e(ex)
view.showGenericErrorMessage()
} finally {
view.hideLoading()
}
}
}
}
package chat.rocket.android.chatinformation.presentation
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel
import chat.rocket.android.core.behaviours.LoadingView
interface MessageInfoView : LoadingView {
fun showGenericErrorMessage()
fun showReadReceipts(messageReceipts: List<ReadReceiptViewModel>)
}
package chat.rocket.android.chatinformation.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.chatinformation.ui.MessageInfoFragment.Companion.TAG_MESSAGE_INFO_FRAGMENT
import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import javax.inject.Inject
fun Context.messageInformationIntent(messageId: String): Intent {
return Intent(this, MessageInfoActivity::class.java).apply {
putExtra(INTENT_MESSAGE_ID, messageId)
}
}
private const val INTENT_MESSAGE_ID = "message_id"
class MessageInfoActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room)
setupToolbar()
val messageId = intent.getStringExtra(INTENT_MESSAGE_ID)
requireNotNull(messageId) { "no message_id provided in Intent extras" }
if (supportFragmentManager.findFragmentByTag(TAG_MESSAGE_INFO_FRAGMENT) == null) {
addFragment(TAG_MESSAGE_INFO_FRAGMENT, R.id.fragment_container) {
newInstance(messageId = messageId)
}
}
}
private fun setupToolbar() {
text_room_name.textContent = getString(R.string.message_information_title)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
toolbar.setNavigationOnClickListener { finishActivity() }
}
private fun finishActivity() {
super.onBackPressed()
overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector
}
}
\ No newline at end of file
package chat.rocket.android.chatinformation.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter
import chat.rocket.android.chatinformation.presentation.MessageInfoPresenter
import chat.rocket.android.chatinformation.presentation.MessageInfoView
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.core.model.ReadReceipt
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_message_info.*
import javax.inject.Inject
fun newInstance(messageId: String): Fragment {
return MessageInfoFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_MESSAGE_ID, messageId)
}
}
}
private const val BUNDLE_MESSAGE_ID = "message_id"
class MessageInfoFragment : Fragment(), MessageInfoView {
@Inject
lateinit var presenter: MessageInfoPresenter
private lateinit var adapter: ReadReceiptAdapter
private lateinit var messageId: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
setHasOptionsMenu(true)
val bundle = arguments
if (bundle != null) {
messageId = bundle.getString(BUNDLE_MESSAGE_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_message_info, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
presenter.loadReadReceipts(messageId = messageId)
}
private fun setupRecyclerView() {
// Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
adapter = ReadReceiptAdapter()
linearLayoutManager.stackFromEnd = true
receipt_list.layoutManager = linearLayoutManager
receipt_list.itemAnimator = DefaultItemAnimator()
receipt_list.adapter = adapter
}
override fun showGenericErrorMessage() {
showToast(R.string.msg_generic_error)
}
override fun showLoading() {
view_loading.setVisible(true)
view_loading.show()
}
override fun hideLoading() {
view_loading.hide()
view_loading.setVisible(false)
}
override fun showReadReceipts(messageReceipts: List<ReadReceiptViewModel>) {
adapter.addAll(messageReceipts)
}
companion object {
const val TAG_MESSAGE_INFO_FRAGMENT = "MessageInfoFragment"
}
}
package chat.rocket.android.chatinformation.viewmodel
data class ReadReceiptViewModel(
val avatar: String,
val name: String,
val time: String
)
\ No newline at end of file
...@@ -78,24 +78,26 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>( ...@@ -78,24 +78,26 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
private val onClickListener = { view: View -> private val onClickListener = { view: View ->
if (data?.message?.isSystemMessage() == false) { if (data?.message?.isSystemMessage() == false) {
data?.message?.let { data?.let { vm ->
val menuItems = view.context.inflate(R.menu.message_actions).toList() vm.message.let {
menuItems.find { it.itemId == R.id.action_message_unpin }?.apply { val menuItems = view.context.inflate(R.menu.message_actions).toList()
setTitle(if (it.pinned) R.string.action_msg_unpin else R.string.action_msg_pin) menuItems.find { it.itemId == R.id.action_message_unpin }?.apply {
isChecked = it.pinned setTitle(if (it.pinned) R.string.action_msg_unpin else R.string.action_msg_pin)
} isChecked = it.pinned
}
menuItems.find { it.itemId == R.id.action_message_star }?.apply { menuItems.find { it.itemId == R.id.action_message_star }?.apply {
val isStarred = it.starred?.isNotEmpty() ?: false val isStarred = it.starred?.isNotEmpty() ?: false
setTitle(if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star) setTitle(if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star)
isChecked = isStarred isChecked = isStarred
} }
view.context?.let { view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) { if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) { with(it.baseContext as AppCompatActivity) {
val actionsBottomSheet = MessageActionsBottomSheet() val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder) actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null) actionsBottomSheet.show(supportFragmentManager, null)
}
} }
} }
} }
......
...@@ -211,6 +211,9 @@ class ChatRoomAdapter( ...@@ -211,6 +211,9 @@ class ChatRoomAdapter(
override fun onActionSelected(item: MenuItem, message: Message) { override fun onActionSelected(item: MenuItem, message: Message) {
message.apply { message.apply {
when (item.itemId) { when (item.itemId) {
R.id.action_message_info -> {
presenter?.messageInfo(id)
}
R.id.action_message_reply -> { R.id.action_message_reply -> {
if (roomName != null && roomType != null) { if (roomName != null && roomType != null) {
presenter?.citeMessage(roomName, roomType, id, true) presenter?.citeMessage(roomName, roomType, id, true)
......
...@@ -4,6 +4,7 @@ import android.graphics.Color ...@@ -4,6 +4,7 @@ import android.graphics.Color
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.MessageUiModel import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
...@@ -39,6 +40,18 @@ class MessageViewHolder( ...@@ -39,6 +40,18 @@ class MessageViewHolder(
text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null
image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false
} }
if (data.unread == null) {
read_receipt_view.isVisible = false
} else {
read_receipt_view.setImageResource(
if (data.unread == true) {
R.drawable.ic_check_unread_24dp
} else {
R.drawable.ic_check_read_24dp
}
)
read_receipt_view.isVisible = true
}
} }
} }
} }
\ No newline at end of file
...@@ -33,4 +33,4 @@ class ChatRoomFragmentModule { ...@@ -33,4 +33,4 @@ class ChatRoomFragmentModule {
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
} }
\ No newline at end of file
package chat.rocket.android.chatroom.domain package chat.rocket.android.chatroom.domain
data class MessageReply( data class MessageReply(val roomName: String, val permalink: String)
val roomName: String, \ No newline at end of file
val permalink: String
)
\ No newline at end of file
package chat.rocket.android.chatroom.presentation package chat.rocket.android.chatroom.presentation
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatinformation.ui.messageInformationIntent
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.chatRoomIntent import chat.rocket.android.chatroom.ui.chatRoomIntent
import chat.rocket.android.server.ui.changeServerIntent import chat.rocket.android.server.ui.changeServerIntent
...@@ -69,4 +70,9 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) { ...@@ -69,4 +70,9 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
) )
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit) activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
} }
fun toMessageInformation(messageId: String) {
activity.startActivity(activity.messageInformationIntent(messageId = messageId))
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
}
} }
\ No newline at end of file
...@@ -153,4 +153,4 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -153,4 +153,4 @@ interface ChatRoomView : LoadingView, MessageView {
* to reply. * to reply.
*/ */
fun openDirectMessage(chatRoom: ChatRoom, permalink: String) fun openDirectMessage(chatRoom: ChatRoom, permalink: String)
} }
\ No newline at end of file
...@@ -12,6 +12,7 @@ import android.text.SpannableStringBuilder ...@@ -12,6 +12,7 @@ import android.text.SpannableStringBuilder
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
...@@ -147,6 +148,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -147,6 +148,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private lateinit var actionSnackbar: ActionSnackbar private lateinit var actionSnackbar: ActionSnackbar
internal var citation: String? = null internal var citation: String? = null
private var editingMessageId: String? = null private var editingMessageId: String? = null
internal var disableMenu: Boolean = false
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
private var playComposeMessageButtonsAnimation = true private var playComposeMessageButtonsAnimation = true
...@@ -249,7 +251,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -249,7 +251,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
setReactionButtonIcon(R.drawable.ic_reaction_24dp) setReactionButtonIcon(R.drawable.ic_reaction_24dp)
emojiKeyboardPopup.dismiss() dismissEmojiKeyboard()
activity?.invalidateOptionsMenu()
}
private fun dismissEmojiKeyboard() {
// Check if the keyboard was ever initialized.
// It may be the case when you are looking a not joined room
if (::emojiKeyboardPopup.isInitialized) {
emojiKeyboardPopup.dismiss()
}
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
...@@ -320,6 +331,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -320,6 +331,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
recycler_view.addOnLayoutChangeListener(layoutChangeListener) recycler_view.addOnLayoutChangeListener(layoutChangeListener)
recycler_view.addOnScrollListener(onScrollListener) recycler_view.addOnScrollListener(onScrollListener)
// Load just once, on the first page...
presenter.loadActiveMembers(chatRoomId, chatRoomType, filterSelfOut = true)
} }
val oldMessagesCount = adapter.itemCount val oldMessagesCount = adapter.itemCount
...@@ -350,7 +364,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -350,7 +364,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui { ui {
setupMessageComposer(userCanPost) setupMessageComposer(userCanPost)
isBroadcastChannel = channelIsBroadcast isBroadcastChannel = channelIsBroadcast
if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu() if (isBroadcastChannel && !userCanMod) {
disableMenu = true
activity?.invalidateOptionsMenu()
}
} }
} }
...@@ -934,4 +951,4 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -934,4 +951,4 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupToolbar(toolbarTitle: String) { private fun setupToolbar(toolbarTitle: String) {
(activity as ChatRoomActivity).showToolbarTitle(toolbarTitle) (activity as ChatRoomActivity).showToolbarTitle(toolbarTitle)
} }
} }
\ No newline at end of file
...@@ -14,7 +14,21 @@ internal fun ChatRoomFragment.setupMenu(menu: Menu) { ...@@ -14,7 +14,21 @@ internal fun ChatRoomFragment.setupMenu(menu: Menu) {
setupSearchMessageMenuItem(menu, requireContext()) setupSearchMessageMenuItem(menu, requireContext())
setupFavoriteMenuItem(menu) setupFavoriteMenuItem(menu)
if (chatRoomType != RoomType.DIRECT_MESSAGE) { menu.add(
Menu.NONE,
MENU_ACTION_PINNED_MESSAGES,
Menu.NONE,
R.string.title_pinned_messages
)
menu.add(
Menu.NONE,
MENU_ACTION_FAVORITE_MESSAGES,
Menu.NONE,
R.string.title_favorite_messages
)
if (chatRoomType != RoomType.DIRECT_MESSAGE && !disableMenu) {
menu.add( menu.add(
Menu.NONE, Menu.NONE,
MENU_ACTION_MEMBER, MENU_ACTION_MEMBER,
...@@ -30,26 +44,14 @@ internal fun ChatRoomFragment.setupMenu(menu: Menu) { ...@@ -30,26 +44,14 @@ internal fun ChatRoomFragment.setupMenu(menu: Menu) {
) )
} }
menu.add( if (!disableMenu) {
Menu.NONE, menu.add(
MENU_ACTION_PINNED_MESSAGES, Menu.NONE,
Menu.NONE, MENU_ACTION_FILES,
R.string.title_pinned_messages Menu.NONE,
) R.string.title_files
)
menu.add( }
Menu.NONE,
MENU_ACTION_FAVORITE_MESSAGES,
Menu.NONE,
R.string.title_favorite_messages
)
menu.add(
Menu.NONE,
MENU_ACTION_FILES,
Menu.NONE,
R.string.title_files
)
} }
internal fun ChatRoomFragment.setOnMenuItemClickListener(item: MenuItem) { internal fun ChatRoomFragment.setOnMenuItemClickListener(item: MenuItem) {
...@@ -101,7 +103,7 @@ private fun ChatRoomFragment.setupSearchViewTextListener(searchView: SearchView) ...@@ -101,7 +103,7 @@ private fun ChatRoomFragment.setupSearchViewTextListener(searchView: SearchView)
// TODO: We use isSearchTermQueried to avoid querying when the search view is expanded but the user doesn't start typing. Check for a native solution. // TODO: We use isSearchTermQueried to avoid querying when the search view is expanded but the user doesn't start typing. Check for a native solution.
if (it.isEmpty() && isSearchTermQueried) { if (it.isEmpty() && isSearchTermQueried) {
presenter.loadMessages(chatRoomId, chatRoomType, clearDataSet = true) presenter.loadMessages(chatRoomId, chatRoomType, clearDataSet = true)
} else if (it.isNotEmpty()){ } else if (it.isNotEmpty()) {
presenter.searchMessages(chatRoomId, it) presenter.searchMessages(chatRoomId, it)
isSearchTermQueried = true isSearchTermQueried = true
} }
......
...@@ -14,7 +14,9 @@ data class AudioAttachmentUiModel( ...@@ -14,7 +14,9 @@ data class AudioAttachmentUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseFileAttachmentUiModel<AudioAttachment> { ) : BaseFileAttachmentUiModel<AudioAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.AUDIO_ATTACHMENT.viewType get() = BaseUiModel.ViewType.AUDIO_ATTACHMENT.viewType
......
...@@ -16,7 +16,9 @@ data class AuthorAttachmentUiModel( ...@@ -16,7 +16,9 @@ data class AuthorAttachmentUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseAttachmentUiModel<AuthorAttachment> { ) : BaseAttachmentUiModel<AuthorAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.AUTHOR_ATTACHMENT.viewType get() = BaseUiModel.ViewType.AUTHOR_ATTACHMENT.viewType
......
...@@ -13,6 +13,8 @@ interface BaseUiModel<out T> { ...@@ -13,6 +13,8 @@ interface BaseUiModel<out T> {
var nextDownStreamMessage: BaseUiModel<*>? var nextDownStreamMessage: BaseUiModel<*>?
var preview: Message? var preview: Message?
var isTemporary: Boolean var isTemporary: Boolean
var unread: Boolean?
var menuItemsToHide: MutableList<Int>
enum class ViewType(val viewType: Int) { enum class ViewType(val viewType: Int) {
MESSAGE(0), MESSAGE(0),
......
...@@ -15,7 +15,9 @@ data class ColorAttachmentUiModel( ...@@ -15,7 +15,9 @@ data class ColorAttachmentUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean?,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseAttachmentUiModel<ColorAttachment> { ) : BaseAttachmentUiModel<ColorAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.COLOR_ATTACHMENT.viewType get() = BaseUiModel.ViewType.COLOR_ATTACHMENT.viewType
......
...@@ -14,7 +14,9 @@ data class GenericFileAttachmentUiModel( ...@@ -14,7 +14,9 @@ data class GenericFileAttachmentUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseFileAttachmentUiModel<GenericFileAttachment> { ) : BaseFileAttachmentUiModel<GenericFileAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType get() = BaseUiModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType
......
...@@ -16,7 +16,9 @@ data class ImageAttachmentUiModel( ...@@ -16,7 +16,9 @@ data class ImageAttachmentUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseFileAttachmentUiModel<ImageAttachment> { ) : BaseFileAttachmentUiModel<ImageAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.IMAGE_ATTACHMENT.viewType get() = BaseUiModel.ViewType.IMAGE_ATTACHMENT.viewType
......
...@@ -15,7 +15,9 @@ data class MessageAttachmentUiModel( ...@@ -15,7 +15,9 @@ data class MessageAttachmentUiModel(
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
var messageLink: String? = null, var messageLink: String? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseUiModel<Message> { ) : BaseUiModel<Message> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.MESSAGE_ATTACHMENT.viewType get() = BaseUiModel.ViewType.MESSAGE_ATTACHMENT.viewType
......
...@@ -5,13 +5,15 @@ import chat.rocket.android.chatroom.domain.MessageReply ...@@ -5,13 +5,15 @@ import chat.rocket.android.chatroom.domain.MessageReply
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
data class MessageReplyUiModel( data class MessageReplyUiModel(
override val rawData: MessageReply, override val rawData: MessageReply,
override val messageId: String, override val messageId: String,
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>?, override var nextDownStreamMessage: BaseUiModel<*>?,
override var preview: Message?, override var preview: Message?,
override var isTemporary: Boolean = false, override var isTemporary: Boolean = false,
override val message: Message override val message: Message,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseUiModel<MessageReply> { ) : BaseUiModel<MessageReply> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.MESSAGE_REPLY.viewType get() = BaseUiModel.ViewType.MESSAGE_REPLY.viewType
......
...@@ -4,19 +4,21 @@ import chat.rocket.android.R ...@@ -4,19 +4,21 @@ import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
data class MessageUiModel( data class MessageUiModel(
override val message: Message, override val message: Message,
override val rawData: Message, override val rawData: Message,
override val messageId: String, override val messageId: String,
override val avatar: String, override val avatar: String,
override val time: CharSequence, override val time: CharSequence,
override val senderName: CharSequence, override val senderName: CharSequence,
override val content: CharSequence, override val content: CharSequence,
override val isPinned: Boolean, override val isPinned: Boolean,
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
var isFirstUnread: Boolean, override var unread: Boolean? = null,
override var isTemporary: Boolean = false var isFirstUnread: Boolean,
override var isTemporary: Boolean = false,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseMessageUiModel<Message> { ) : BaseMessageUiModel<Message> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.MESSAGE.viewType get() = BaseUiModel.ViewType.MESSAGE.viewType
......
...@@ -15,7 +15,9 @@ data class UrlPreviewUiModel( ...@@ -15,7 +15,9 @@ data class UrlPreviewUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseUiModel<Url> { ) : BaseUiModel<Url> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.URL_PREVIEW.viewType get() = BaseUiModel.ViewType.URL_PREVIEW.viewType
......
...@@ -14,7 +14,9 @@ data class VideoAttachmentUiModel( ...@@ -14,7 +14,9 @@ data class VideoAttachmentUiModel(
override var reactions: List<ReactionUiModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null,
override var menuItemsToHide: MutableList<Int> = mutableListOf()
) : BaseFileAttachmentUiModel<VideoAttachment> { ) : BaseFileAttachmentUiModel<VideoAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseUiModel.ViewType.VIDEO_ATTACHMENT.viewType get() = BaseUiModel.ViewType.VIDEO_ATTACHMENT.viewType
......
...@@ -68,13 +68,15 @@ class RoomUiModelMapper( ...@@ -68,13 +68,15 @@ class RoomUiModelMapper(
val name = mapName(user.username!!, user.name, false) val name = mapName(user.username!!, user.name, false)
val status = user.status val status = user.status
val avatar = serverUrl.avatarUrl(user.username!!) val avatar = serverUrl.avatarUrl(user.username!!)
val username = user.username!!
RoomUiModel( RoomUiModel(
id = user.id, id = user.id,
name = name, name = name,
type = roomTypeOf(RoomType.DIRECT_MESSAGE), type = roomTypeOf(RoomType.DIRECT_MESSAGE),
avatar = avatar, avatar = avatar,
status = status status = status,
username = username
) )
} }
} }
...@@ -97,7 +99,7 @@ class RoomUiModelMapper( ...@@ -97,7 +99,7 @@ class RoomUiModelMapper(
val isUnread = alert || unread > 0 val isUnread = alert || unread > 0
val type = roomTypeOf(type) val type = roomTypeOf(type)
val status = chatRoom.status?.let { userStatusOf(it) } val status = chatRoom.status?.let { userStatusOf(it) }
val roomName = mapName(name, chatRoom.userFullname, isUnread) val roomName = mapName(name, fullname, isUnread)
val timestamp = mapDate(lastMessageTimestamp ?: updatedAt, isUnread) val timestamp = mapDate(lastMessageTimestamp ?: updatedAt, isUnread)
val avatar = if (type is RoomType.DirectMessage) { val avatar = if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name) serverUrl.avatarUrl(name)
...@@ -117,7 +119,8 @@ class RoomUiModelMapper( ...@@ -117,7 +119,8 @@ class RoomUiModelMapper(
unread = unread, unread = unread,
alert = isUnread, alert = isUnread,
lastMessage = lastMessage, lastMessage = lastMessage,
status = status status = status,
username = if (type is RoomType.DirectMessage) name else null
) )
} }
} }
......
...@@ -12,5 +12,6 @@ data class RoomUiModel( ...@@ -12,5 +12,6 @@ data class RoomUiModel(
val unread: String? = null, val unread: String? = null,
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
) )
\ No newline at end of file
...@@ -9,9 +9,7 @@ import chat.rocket.android.chatrooms.ui.ChatRoomsFragment ...@@ -9,9 +9,7 @@ import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.infraestructure.ConnectionManager import chat.rocket.android.server.infraestructure.ConnectionManager
...@@ -37,13 +35,6 @@ class ChatRoomsFragmentModule { ...@@ -37,13 +35,6 @@ class ChatRoomsFragmentModule {
return frag return frag
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides @Provides
@PerFragment @PerFragment
fun provideRocketChatClient( fun provideRocketChatClient(
...@@ -53,15 +44,6 @@ class ChatRoomsFragmentModule { ...@@ -53,15 +44,6 @@ class ChatRoomsFragmentModule {
return factory.create(currentServer) return factory.create(currentServer)
} }
@Provides
@PerFragment
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides @Provides
@PerFragment @PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao() fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
......
...@@ -18,7 +18,7 @@ class FetchChatRoomsInteractor( ...@@ -18,7 +18,7 @@ class FetchChatRoomsInteractor(
suspend fun refreshChatRooms() { suspend fun refreshChatRooms() {
val rooms = retryIO("fetch chatRooms", times = 10, val rooms = retryIO("fetch chatRooms", times = 10,
initialDelay = 200, maxDelay = 2000) { initialDelay = 200, maxDelay = 2000) {
client.chatRooms().update.map { room -> client.chatRooms().update.map { room ->
mapChatRoom(room) mapChatRoom(room)
} }
...@@ -50,27 +50,28 @@ class FetchChatRoomsInteractor( ...@@ -50,27 +50,28 @@ class FetchChatRoomsInteractor(
} }
} }
return ChatRoomEntity( return ChatRoomEntity(
id = id, id = id,
subscriptionId = subscriptionId, subscriptionId = subscriptionId,
type = type.toString(), type = type.toString(),
name = name, name = name,
fullname = fullName, fullname = fullName,
userId = userId, userId = userId,
ownerId = user?.id, ownerId = user?.id,
readonly = readonly, readonly = readonly,
isDefault = default, isDefault = default,
favorite = favorite, favorite = favorite,
open = open, open = open,
alert = alert, alert = alert,
unread = unread, unread = unread,
userMentions = userMentions, userMentions = userMentions,
groupMentions = groupMentions, groupMentions = groupMentions,
updatedAt = updatedAt, updatedAt = updatedAt,
timestamp = timestamp, timestamp = timestamp,
lastSeen = lastSeen, lastSeen = lastSeen,
lastMessageText = lastMessage?.message, lastMessageText = lastMessage?.message,
lastMessageUserId = lastMessage?.sender?.id, lastMessageUserId = lastMessage?.sender?.id,
lastMessageTimestamp = lastMessage?.timestamp lastMessageTimestamp = lastMessage?.timestamp,
broadcast = broadcast
) )
} }
} }
......
...@@ -51,7 +51,8 @@ class ChatRoomsPresenter @Inject constructor( ...@@ -51,7 +51,8 @@ class ChatRoomsPresenter @Inject constructor(
id = id, id = id,
subscriptionId = "", subscriptionId = "",
type = type.toString(), type = type.toString(),
name = name.toString(), name = username ?: name.toString(),
fullname = name.toString(),
open = false open = false
) )
loadChatRoom(entity) loadChatRoom(entity)
......
...@@ -8,6 +8,8 @@ import chat.rocket.android.authentication.server.di.ServerFragmentProvider ...@@ -8,6 +8,8 @@ import chat.rocket.android.authentication.server.di.ServerFragmentProvider
import chat.rocket.android.authentication.signup.di.SignupFragmentProvider import chat.rocket.android.authentication.signup.di.SignupFragmentProvider
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.chatinformation.di.MessageInfoFragmentProvider
import chat.rocket.android.chatinformation.ui.MessageInfoActivity
import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
import chat.rocket.android.chatroom.di.ChatRoomModule import chat.rocket.android.chatroom.di.ChatRoomModule
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
...@@ -82,6 +84,8 @@ abstract class ActivityBuilder { ...@@ -82,6 +84,8 @@ abstract class ActivityBuilder {
abstract fun bindChangeServerActivity(): ChangeServerActivity abstract fun bindChangeServerActivity(): ChangeServerActivity
@PerActivity @PerActivity
@ContributesAndroidInjector(modules = [MessageInfoFragmentProvider::class])
abstract fun bindMessageInfoActiviy(): MessageInfoActivity
@ContributesAndroidInjector(modules = [DrawModule::class]) @ContributesAndroidInjector(modules = [DrawModule::class])
abstract fun bindDrawingActivity(): DrawingActivity abstract fun bindDrawingActivity(): DrawingActivity
} }
\ No newline at end of file
...@@ -14,6 +14,8 @@ import chat.rocket.android.authentication.infraestructure.SharedPreferencesToken ...@@ -14,6 +14,8 @@ import chat.rocket.android.authentication.infraestructure.SharedPreferencesToken
import chat.rocket.android.chatroom.service.MessageService import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.qualifier.ForAuthentication import chat.rocket.android.dagger.qualifier.ForAuthentication
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository
...@@ -68,6 +70,7 @@ import ru.noties.markwon.SpannableConfiguration ...@@ -68,6 +70,7 @@ import ru.noties.markwon.SpannableConfiguration
import ru.noties.markwon.spans.SpannableTheme import ru.noties.markwon.spans.SpannableTheme
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Named
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
...@@ -82,7 +85,7 @@ class AppModule { ...@@ -82,7 +85,7 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { val interceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) { override fun log(message: String) {
Timber.d(message) Timber.d(message)
} }
...@@ -254,7 +257,12 @@ class AppModule { ...@@ -254,7 +257,12 @@ class AppModule {
} }
@Provides @Provides
fun provideMessageParser(context: Application, configuration: SpannableConfiguration, serverInteractor: GetCurrentServerInteractor, settingsInteractor: GetSettingsInteractor): MessageParser { fun provideMessageParser(
context: Application,
configuration: SpannableConfiguration,
serverInteractor: GetCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor
): MessageParser {
val url = serverInteractor.get()!! val url = serverInteractor.get()!!
return MessageParser(context, configuration, settingsInteractor.get(url)) return MessageParser(context, configuration, settingsInteractor.get(url))
} }
...@@ -301,4 +309,18 @@ class AppModule { ...@@ -301,4 +309,18 @@ class AppModule {
fun provideJobSchedulerInteractor(jobScheduler: JobScheduler, jobInfo: JobInfo): JobSchedulerInteractor { fun provideJobSchedulerInteractor(jobScheduler: JobScheduler, jobInfo: JobInfo): JobSchedulerInteractor {
return JobSchedulerInteractorImpl(jobScheduler, jobInfo) return JobSchedulerInteractorImpl(jobScheduler, jobInfo)
} }
}
\ No newline at end of file @Provides
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
}
...@@ -115,10 +115,10 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> { ...@@ -115,10 +115,10 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> {
const val TYPE_ORDER = """ const val TYPE_ORDER = """
CASE CASE
WHEN type = '${RoomType.CHANNEL}' THEN 1 WHEN type = 'c' THEN 1
WHEN type = '${RoomType.PRIVATE_GROUP}' THEN 2 WHEN type = 'p' THEN 2
WHEN type = '${RoomType.DIRECT_MESSAGE}' THEN 3 WHEN type = 'd' THEN 3
WHEN type = '${RoomType.LIVECHAT}' THEN 4 WHEN type = 'l' THEN 4
ELSE 5 ELSE 5
END END
""" """
......
...@@ -295,7 +295,8 @@ class DatabaseManager(val context: Application, ...@@ -295,7 +295,8 @@ class DatabaseManager(val context: Application,
lastSeen = subscription.lastSeen, lastSeen = subscription.lastSeen,
lastMessageText = room.lastMessage?.message, lastMessageText = room.lastMessage?.message,
lastMessageUserId = room.lastMessage?.sender?.id, lastMessageUserId = room.lastMessage?.sender?.id,
lastMessageTimestamp = room.lastMessage?.timestamp lastMessageTimestamp = room.lastMessage?.timestamp,
broadcast = room.broadcast
) )
} }
......
...@@ -7,7 +7,7 @@ import chat.rocket.android.db.model.UserEntity ...@@ -7,7 +7,7 @@ import chat.rocket.android.db.model.UserEntity
@Database( @Database(
entities = [UserEntity::class, ChatRoomEntity::class], entities = [UserEntity::class, ChatRoomEntity::class],
version = 3, version = 4,
exportSchema = true exportSchema = true
) )
abstract class RCDatabase : RoomDatabase() { abstract class RCDatabase : RoomDatabase() {
......
...@@ -19,6 +19,7 @@ import androidx.room.PrimaryKey ...@@ -19,6 +19,7 @@ import androidx.room.PrimaryKey
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["lastMessageUserId"]) ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["lastMessageUserId"])
] ]
) )
data class ChatRoomEntity( data class ChatRoomEntity(
@PrimaryKey var id: String, @PrimaryKey var id: String,
var subscriptionId: String, var subscriptionId: String,
...@@ -40,7 +41,8 @@ data class ChatRoomEntity( ...@@ -40,7 +41,8 @@ data class ChatRoomEntity(
var lastSeen: Long? = -1, var lastSeen: Long? = -1,
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
) )
data class ChatRoom( data class ChatRoom(
......
...@@ -22,22 +22,6 @@ class FavoriteMessagesFragmentModule { ...@@ -22,22 +22,6 @@ class FavoriteMessagesFragmentModule {
return frag return frag
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides @Provides
@PerFragment @PerFragment
fun provideJob() = Job() fun provideJob() = Job()
......
...@@ -3,15 +3,11 @@ package chat.rocket.android.files.di ...@@ -3,15 +3,11 @@ package chat.rocket.android.files.di
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.files.presentation.FilesView import chat.rocket.android.files.presentation.FilesView
import chat.rocket.android.files.ui.FilesFragment import chat.rocket.android.files.ui.FilesFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module @Module
class FilesFragmentModule { class FilesFragmentModule {
...@@ -22,22 +18,6 @@ class FilesFragmentModule { ...@@ -22,22 +18,6 @@ class FilesFragmentModule {
return frag return frag
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides @Provides
@PerFragment @PerFragment
fun provideJob() = Job() fun provideJob() = Job()
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor ...@@ -5,6 +5,7 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.common.model.SimpleUser
import chat.rocket.common.model.User import chat.rocket.common.model.User
import javax.inject.Inject import javax.inject.Inject
...@@ -26,6 +27,10 @@ class UserHelper @Inject constructor( ...@@ -26,6 +27,10 @@ class UserHelper @Inject constructor(
return if (settings.useRealName()) user.name ?: user.username else user.username return if (settings.useRealName()) user.name ?: user.username else user.username
} }
fun displayName(user: SimpleUser): String {
return if (settings.useRealName()) user.name ?: user.username ?: "" else user.username ?: ""
}
/** /**
* Return current logged user's display name. * Return current logged user's display name.
* *
......
...@@ -52,9 +52,10 @@ class SharedPreferencesLocalRepository( ...@@ -52,9 +52,10 @@ class SharedPreferencesLocalRepository(
override fun clear(key: String) = preferences.edit { remove(key) } override fun clear(key: String) = preferences.edit { remove(key) }
override fun clearAllFromServer(server: String) { override fun clearAllFromServer(server: String) {
clear(LocalRepository.KEY_PUSH_TOKEN) preferences.all.keys.forEach { key ->
clear(LocalRepository.TOKEN_KEY + server) if (key.startsWith(server, ignoreCase = true)) {
clear(LocalRepository.SETTINGS_KEY + server) clear(key)
clear(LocalRepository.CURRENT_USERNAME_KEY) }
}
} }
} }
\ No newline at end of file
...@@ -4,16 +4,12 @@ import androidx.lifecycle.LifecycleOwner ...@@ -4,16 +4,12 @@ import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.members.presentation.MembersNavigator import chat.rocket.android.members.presentation.MembersNavigator
import chat.rocket.android.members.presentation.MembersView import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.ui.MembersFragment import chat.rocket.android.members.ui.MembersFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module @Module
class MembersFragmentModule { class MembersFragmentModule {
...@@ -28,22 +24,6 @@ class MembersFragmentModule { ...@@ -28,22 +24,6 @@ class MembersFragmentModule {
@PerFragment @PerFragment
fun provideChatRoomNavigator(activity: ChatRoomActivity) = MembersNavigator(activity) fun provideChatRoomNavigator(activity: ChatRoomActivity) = MembersNavigator(activity)
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides @Provides
@PerFragment @PerFragment
fun provideJob() = Job() fun provideJob() = Job()
......
...@@ -5,11 +5,9 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -5,11 +5,9 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.mentions.presentention.MentionsView import chat.rocket.android.mentions.presentention.MentionsView
import chat.rocket.android.mentions.ui.MentionsFragment import chat.rocket.android.mentions.ui.MentionsFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module @Module
class MentionsFragmentModule { class MentionsFragmentModule {
...@@ -20,13 +18,6 @@ class MentionsFragmentModule { ...@@ -20,13 +18,6 @@ class MentionsFragmentModule {
return frag return frag
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides @Provides
@PerFragment @PerFragment
fun provideJob() = Job() fun provideJob() = Job()
......
...@@ -3,15 +3,11 @@ package chat.rocket.android.pinnedmessages.di ...@@ -3,15 +3,11 @@ package chat.rocket.android.pinnedmessages.di
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView
import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module @Module
class PinnedMessagesFragmentModule { class PinnedMessagesFragmentModule {
...@@ -22,22 +18,6 @@ class PinnedMessagesFragmentModule { ...@@ -22,22 +18,6 @@ class PinnedMessagesFragmentModule {
return frag return frag
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides @Provides
@PerFragment @PerFragment
fun provideJob() = Job() fun provideJob() = Job()
......
...@@ -25,7 +25,7 @@ class RefreshSettingsInteractor @Inject constructor( ...@@ -25,7 +25,7 @@ class RefreshSettingsInteractor @Inject constructor(
HIDE_USER_JOIN, HIDE_USER_LEAVE, HIDE_USER_JOIN, HIDE_USER_LEAVE,
HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ALLOW_MESSAGE_DELETING, HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ALLOW_MESSAGE_DELETING,
ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, ALLOW_MESSAGE_STARRING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS, ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, ALLOW_MESSAGE_STARRING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS,
WIDE_TILE_310, STORE_LAST_MESSAGE) WIDE_TILE_310, STORE_LAST_MESSAGE, MESSAGE_READ_RECEIPT_ENABLED, MESSAGE_READ_RECEIPT_STORE_USERS)
suspend fun refresh(server: String) { suspend fun refresh(server: String) {
withContext(CommonPool) { withContext(CommonPool) {
......
...@@ -46,6 +46,8 @@ const val SHOW_EDITED_STATUS = "Message_ShowEditedStatus" ...@@ -46,6 +46,8 @@ const val SHOW_EDITED_STATUS = "Message_ShowEditedStatus"
const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning" const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning"
const val ALLOW_MESSAGE_STARRING = "Message_AllowStarring" const val ALLOW_MESSAGE_STARRING = "Message_AllowStarring"
const val STORE_LAST_MESSAGE = "Store_Last_Message" const val STORE_LAST_MESSAGE = "Store_Last_Message"
const val MESSAGE_READ_RECEIPT_ENABLED = "Message_Read_Receipt_Enabled"
const val MESSAGE_READ_RECEIPT_STORE_USERS = "Message_Read_Receipt_Store_Users"
/* /*
* Extension functions for Public Settings. * Extension functions for Public Settings.
...@@ -87,6 +89,9 @@ fun PublicSettings.allowedMessageDeleting(): Boolean = this[ALLOW_MESSAGE_DELETI ...@@ -87,6 +89,9 @@ fun PublicSettings.allowedMessageDeleting(): Boolean = this[ALLOW_MESSAGE_DELETI
fun PublicSettings.hasShowLastMessage(): Boolean = this[STORE_LAST_MESSAGE] != null fun PublicSettings.hasShowLastMessage(): Boolean = this[STORE_LAST_MESSAGE] != null
fun PublicSettings.showLastMessage(): Boolean = this[STORE_LAST_MESSAGE]?.value == true fun PublicSettings.showLastMessage(): Boolean = this[STORE_LAST_MESSAGE]?.value == true
fun PublicSettings.messageReadReceiptEnabled(): Boolean = this[MESSAGE_READ_RECEIPT_ENABLED]?.value == true
fun PublicSettings.messageReadReceiptStoreUsers(): Boolean = this[MESSAGE_READ_RECEIPT_STORE_USERS]?.value == true
fun PublicSettings.uploadMimeTypeFilter(): Array<String>? { fun PublicSettings.uploadMimeTypeFilter(): Array<String>? {
val values = this[UPLOAD_WHITELIST_MIMETYPES]?.value as String? val values = this[UPLOAD_WHITELIST_MIMETYPES]?.value as String?
if (!values.isNullOrBlank()) { if (!values.isNullOrBlank()) {
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/actionMenuColor"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="@color/actionMenuColor"
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#1D74F5"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF1D74F5"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#999999"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF999999"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>
<?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:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".chatinformation.ui.MessageInfoActivity">
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/text_read_by"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="18sp"
android:text="@string/read_by"
app:layout_constraintBottom_toTopOf="@+id/receipt_list"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/receipt_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_read_by" />
</androidx.constraintlayout.widget.ConstraintLayout>
...@@ -98,12 +98,24 @@ ...@@ -98,12 +98,24 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:src="@drawable/ic_action_message_star_24dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/text_sender" app:layout_constraintBottom_toBottomOf="@+id/text_sender"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/text_edit_indicator" app:layout_constraintStart_toEndOf="@+id/text_edit_indicator"
app:layout_constraintTop_toTopOf="@+id/text_sender" app:layout_constraintTop_toTopOf="@+id/text_sender"
app:srcCompat="@drawable/ic_action_message_star_24dp"
tools:visibility="visible" />
<ImageView
android:id="@+id/read_receipt_view"
android:layout_width="24dp"
android:layout_height="24dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/text_sender"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/image_star_indicator"
app:layout_constraintTop_toTopOf="@+id/text_sender"
app:srcCompat="@drawable/ic_check_unread_24dp"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView <TextView
...@@ -112,9 +124,9 @@ ...@@ -112,9 +124,9 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="2dp" android:layout_marginBottom="2dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="4dp"
app:layout_constraintStart_toStartOf="@+id/text_sender" app:layout_constraintStart_toStartOf="@+id/text_sender"
app:layout_constraintTop_toBottomOf="@+id/text_sender" app:layout_constraintTop_toBottomOf="@+id/text_sender"
tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" /> tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" />
......
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_marginTop="8dp">
<TextView
android:id="@+id/receipt_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/receipt_time"
app:layout_constraintStart_toEndOf="@+id/avatar_layout"
app:layout_constraintTop_toTopOf="parent"
tools:text="John Doe" />
<include
android:id="@+id/avatar_layout"
layout="@layout/avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/receipt_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:gravity="center"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="04/06/2018 14:18:36" />
</android.support.constraint.ConstraintLayout>
...@@ -2,6 +2,11 @@ ...@@ -2,6 +2,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_message_info"
android:icon="@drawable/ic_action_message_info_outline_24dp"
android:title="@string/action_msg_info" />
<item <item
android:id="@+id/action_message_reply" android:id="@+id/action_message_reply"
android:icon="@drawable/ic_action_message_reply_24dp" android:icon="@drawable/ic_action_message_reply_24dp"
......
...@@ -151,6 +151,7 @@ ...@@ -151,6 +151,7 @@
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Respuesta</string> <string name="action_msg_reply">Respuesta</string>
<string name="action_msg_info">Información del mensaje</string>
<string name="action_msg_edit">Editar</string> <string name="action_msg_edit">Editar</string>
<string name="action_msg_copy">Copiar</string> <string name="action_msg_copy">Copiar</string>
<string name="action_msg_quote">Citar</string> <string name="action_msg_quote">Citar</string>
...@@ -158,8 +159,7 @@ ...@@ -158,8 +159,7 @@
<string name="action_msg_pin">Fijar mensaje</string> <string name="action_msg_pin">Fijar mensaje</string>
<string name="action_msg_unpin">Soltar mensaje</string> <string name="action_msg_unpin">Soltar mensaje</string>
<string name="action_msg_star">Star mensaje</string> <string name="action_msg_star">Star mensaje</string>
// TODO: Add proper translation. <string name="action_msg_unstar">Unstar mensaje</string>
<string name="action_msg_unstar">Unstar Message</string>
<string name="action_msg_share">Compartir</string> <string name="action_msg_share">Compartir</string>
<string name="action_title_editing">Edición de mensaje</string> <string name="action_title_editing">Edición de mensaje</string>
<string name="action_msg_add_reaction">Añadir una reacción</string> <string name="action_msg_add_reaction">Añadir una reacción</string>
...@@ -262,4 +262,7 @@ ...@@ -262,4 +262,7 @@
<string name="notif_success_sending">Mensaje enviado a %1$s!</string> <string name="notif_success_sending">Mensaje enviado a %1$s!</string>
<string name="share_label">Editar mensaje compartido</string> <string name="share_label">Editar mensaje compartido</string>
<string name="read_by">Leído por</string>
<string name="message_information_title">Información del mensaje</string>
<string name="msg_log_out">Saliendo de tu cuenta…</string>
</resources> </resources>
...@@ -172,16 +172,15 @@ ...@@ -172,16 +172,15 @@
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Répondre</string> <string name="action_msg_reply">Répondre</string>
<string name="action_msg_info">Informations sur le message</string>
<string name="action_msg_edit">Modifier</string> <string name="action_msg_edit">Modifier</string>
<string name="action_msg_copy">Copier</string> <string name="action_msg_copy">Copier</string>
<string name="action_msg_quote">Citation</string> <string name="action_msg_quote">Citation</string>
<string name="action_msg_delete">Effacer</string> <string name="action_msg_delete">Effacer</string>
<string name="action_msg_pin">Épingle message</string> <string name="action_msg_pin">Épingle message</string>
<string name="action_msg_unpin">Enlever message</string> <string name="action_msg_unpin">Enlever message</string>
// TODO: Add proper translation. <string name="action_msg_star">Ajouter aux Favoris</string>
<string name="action_msg_star">Star message</string> <string name="action_msg_unstar">Retirer des favoris</string>
// TODO: Add proper translation.
<string name="action_msg_unstar">Unstar Message</string>
<string name="action_msg_share">Partager</string> <string name="action_msg_share">Partager</string>
<string name="action_title_editing">Modification du message</string> <string name="action_title_editing">Modification du message</string>
<string name="action_msg_add_reaction">Ajouter une réaction</string> <string name="action_msg_add_reaction">Ajouter une réaction</string>
...@@ -291,6 +290,7 @@ ...@@ -291,6 +290,7 @@
<string name="notif_action_reply_hint">RÉPONDRE</string> <string name="notif_action_reply_hint">RÉPONDRE</string>
<string name="notif_error_sending">La réponse a échoué. Veuillez réessayer.</string> <string name="notif_error_sending">La réponse a échoué. Veuillez réessayer.</string>
<string name="notif_success_sending">Message envoyé à %1$s!</string> <string name="notif_success_sending">Message envoyé à %1$s!</string>
<!-- TODO Add proper translation--> <string name="read_by">Lire par</string>
<string name="msg_log_out">Logging out…</string> <string name="message_information_title">Informations sur le message</string>
<string name="msg_log_out">Déconnecter…</string>
</resources> </resources>
...@@ -156,6 +156,7 @@ ...@@ -156,6 +156,7 @@
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">जवाब दें</string> <string name="action_msg_reply">जवाब दें</string>
<string name="action_msg_info">संदेश जानकारी</string>
<string name="action_msg_edit">संपादन करें</string> <string name="action_msg_edit">संपादन करें</string>
<string name="action_msg_copy">कॉपी</string> <string name="action_msg_copy">कॉपी</string>
<string name="action_msg_quote">उद्धरण</string> <string name="action_msg_quote">उद्धरण</string>
...@@ -268,6 +269,7 @@ ...@@ -268,6 +269,7 @@
<string name="notif_action_reply_hint">जवाब</string> <string name="notif_action_reply_hint">जवाब</string>
<string name="notif_error_sending">उत्तर विफल हुआ है। कृपया फिर से प्रयास करें।</string> <string name="notif_error_sending">उत्तर विफल हुआ है। कृपया फिर से प्रयास करें।</string>
<string name="notif_success_sending">संदेश भेजा गया %1$s!</string> <string name="notif_success_sending">संदेश भेजा गया %1$s!</string>
<!-- TODO Add proper translation--> <string name="read_by">द्वारा पढ़ें</string>
<string name="msg_log_out">Logging out…</string> <string name="message_information_title">संदेश की जानकारी</string>
</resources> <string name="msg_log_out">लॉग आउट कर रहा हूं…</string>
\ No newline at end of file </resources>
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Responder</string> <string name="action_msg_reply">Responder</string>
<string name="action_msg_info">Informações da mensagem</string>
<string name="action_msg_edit">Editar</string> <string name="action_msg_edit">Editar</string>
<string name="action_msg_copy">Copiar</string> <string name="action_msg_copy">Copiar</string>
<string name="action_msg_quote">Citar</string> <string name="action_msg_quote">Citar</string>
...@@ -268,5 +269,7 @@ ...@@ -268,5 +269,7 @@
<string name="notif_action_reply_hint">RESPONDER</string> <string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">Falha ao enviar a mensagem.</string> <string name="notif_error_sending">Falha ao enviar a mensagem.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string> <string name="notif_success_sending">Mensagem enviada para %1$s!</string>
<string name="read_by">Lida por</string>
<string name="message_information_title">Informações da mensagem</string>
<string name="msg_log_out">Deslogando…</string> <string name="msg_log_out">Deslogando…</string>
</resources> </resources>
...@@ -153,6 +153,7 @@ ...@@ -153,6 +153,7 @@
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Ответить</string> <string name="action_msg_reply">Ответить</string>
<string name="action_msg_info">Інформація про повідомлення</string>
<string name="action_msg_edit">Редактировать</string> <string name="action_msg_edit">Редактировать</string>
<string name="action_msg_copy">Копировать</string> <string name="action_msg_copy">Копировать</string>
<string name="action_msg_quote">Цитата</string> <string name="action_msg_quote">Цитата</string>
......
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Reply</string> <string name="action_msg_reply">Reply</string>
<string name="action_msg_info">Message info</string>
<string name="action_msg_edit">Edit</string> <string name="action_msg_edit">Edit</string>
<string name="action_msg_copy">Copy</string> <string name="action_msg_copy">Copy</string>
<string name="action_msg_quote">Quote</string> <string name="action_msg_quote">Quote</string>
...@@ -268,4 +269,6 @@ ...@@ -268,4 +269,6 @@
<string name="notif_action_reply_hint">REPLY</string> <string name="notif_action_reply_hint">REPLY</string>
<string name="notif_error_sending">Reply has failed. Please try again.</string> <string name="notif_error_sending">Reply has failed. Please try again.</string>
<string name="notif_success_sending">Message sent to %1$s!</string> <string name="notif_success_sending">Message sent to %1$s!</string>
<string name="read_by">Read by</string>
<string name="message_information_title">Message information</string>
</resources> </resources>
\ No newline at end of file
...@@ -10,7 +10,7 @@ buildscript { ...@@ -10,7 +10,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.0-beta02' classpath 'com.android.tools.build:gradle:3.3.0-alpha03'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}" classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:3.2.0' classpath 'com.google.gms:google-services:3.2.0'
......
...@@ -59,7 +59,7 @@ ext { ...@@ -59,7 +59,7 @@ ext {
mockito : '2.10.0' mockito : '2.10.0'
] ]
libraries = [ libraries = [
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}", kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
coroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}", coroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}",
coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutine}", coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutine}",
......
...@@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME ...@@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-rc-2-all.zip
distributionSha256Sum=9af7345c199f1731c187c96d3fe3d31f5405192a42046bafa71d846c3d9adacb distributionSha256Sum=9bc16f929e5920a9a27fef22c65a8ab8140d705e418de0159ca6eb7f08a74200
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