Commit 90d88377 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.Android into new/send-typing-status

parents 8e803b79 9f55d568
......@@ -76,7 +76,7 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
fun onActionSelected(item: MenuItem, message: Message)
}
private val longClickListener = { view: View ->
private val onClickListener = { view: View ->
if (data?.message?.isSystemMessage() == false) {
val menuItems = view.context.inflate(R.menu.message_actions).toList()
menuItems.find { it.itemId == R.id.action_menu_msg_pin_unpin }?.apply {
......@@ -87,11 +87,11 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
val adapter = ActionListAdapter(menuItems, this@BaseViewHolder)
BottomSheetMenu(adapter).show(view.context)
}
true
}
internal fun setupActionMenu(view: View) {
if (listener.isActionsEnabled()) {
view.setOnClickListener(onClickListener)
if (view is ViewGroup) {
for (child in view.children) {
if (child !is RecyclerView && child.id != R.id.recycler_view_reactions) {
......@@ -99,7 +99,6 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
}
}
}
view.setOnLongClickListener(longClickListener)
}
}
......
......@@ -12,6 +12,7 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewMo
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.username
import chat.rocket.android.server.domain.*
......@@ -47,12 +48,13 @@ class ChatRoomPresenter @Inject constructor(
private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy,
private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val permissions: GetPermissionsInteractor,
private val permissions: PermissionsInteractor,
private val uriInteractor: UriInteractor,
private val messagesRepository: MessagesRepository,
private val usersRepository: UsersRepository,
private val roomsRepository: RoomRepository,
private val localRepository: LocalRepository,
private val userHelper: UserHelper,
private val mapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor,
getSettingsInteractor: GetSettingsInteractor,
......@@ -71,6 +73,13 @@ class ChatRoomPresenter @Inject constructor(
private val stateChannel = Channel<State>()
private var lastState = manager.state
fun setupChatRoom() {
launchUI(strategy) {
val canPost = permissions.canPostToReadOnlyChannels()
view.onRoomChanged(canPost)
}
}
fun loadMessages(chatRoomId: String, chatRoomType: String, offset: Long = 0) {
this.chatRoomId = chatRoomId
this.chatRoomType = chatRoomType
......@@ -128,7 +137,7 @@ class ChatRoomPresenter @Inject constructor(
// ignore message for now, will receive it on the stream
val id = UUID.randomUUID().toString()
val message = if (messageId == null) {
val username = localRepository.username()
val username = userHelper.username()
val newMessage = Message(
id = id,
roomId = chatRoomId,
......@@ -549,7 +558,8 @@ class ChatRoomPresenter @Inject constructor(
launchUI(strategy) {
try {
retryIO("joinChat($chatRoomId)") { client.joinChat(chatRoomId) }
view.onJoined()
val canPost = permissions.canPostToReadOnlyChannels()
view.onJoined(canPost)
} catch (ex: RocketChatException) {
Timber.e(ex)
}
......@@ -562,7 +572,7 @@ class ChatRoomPresenter @Inject constructor(
fun react(messageId: String, emoji: String) {
launchUI(strategy) {
try {
retryIO("toogleEmoji($messageId, $emoji)") {
retryIO("toggleEmoji($messageId, $emoji)") {
client.toggleReaction(messageId, emoji.removeSurrounding(":"))
}
} catch (ex: RocketChatException) {
......
......@@ -109,8 +109,10 @@ interface ChatRoomView : LoadingView, MessageView {
fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionViewModel>)
/**
* This user has joined the chat callback.
*
* @param canPost Whether the user can post a message or not.
*/
fun onJoined()
fun onJoined(canPost: Boolean)
fun showReactionsPopup(messageId: String)
......@@ -120,4 +122,6 @@ interface ChatRoomView : LoadingView, MessageView {
* @param commands The list of available commands.
*/
fun populateCommandSuggestions(commands: List<CommandSuggestionViewModel>)
fun onRoomChanged(canPost: Boolean)
}
\ No newline at end of file
......@@ -27,22 +27,25 @@ fun Context.chatRoomIntent(
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean = true
isChatRoomSubscribed: Boolean = true,
isChatRoomOwner: Boolean = false
): Intent {
return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
putExtra(INTENT_IS_CHAT_ROOM_READ_ONLY, isChatRoomReadOnly)
putExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, isChatRoomReadOnly)
putExtra(INTENT_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putExtra(INTENT_CHAT_IS_SUBSCRIBED, isChatRoomSubscribed)
putExtra(INTENT_CHAT_ROOM_IS_OWNER, isChatRoomOwner)
}
}
private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
private const val INTENT_CHAT_ROOM_NAME = "chat_room_name"
private const val INTENT_CHAT_ROOM_TYPE = "chat_room_type"
private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val INTENT_CHAT_ROOM_IS_READ_ONLY = "chat_room_is_read_only"
private const val INTENT_CHAT_ROOM_IS_OWNER = "chat_room_is_owner"
private const val INTENT_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private const val INTENT_CHAT_IS_SUBSCRIBED = "is_chat_room_subscribed"
......@@ -59,6 +62,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
private lateinit var chatRoomType: String
private var isChatRoomReadOnly: Boolean = false
private var isChatRoomSubscribed: Boolean = true
private var isChatRoomOwner: Boolean = false
private var chatRoomLastSeen: Long = -1L
override fun onCreate(savedInstanceState: Bundle?) {
......@@ -84,8 +88,11 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" }
isChatRoomReadOnly = intent.getBooleanExtra(INTENT_IS_CHAT_ROOM_READ_ONLY, true)
requireNotNull(chatRoomType) { "no is_chat_room_read_only provided in Intent extras" }
isChatRoomReadOnly = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true)
requireNotNull(isChatRoomReadOnly) { "no chat_room_is_read_only provided in Intent extras" }
isChatRoomOwner = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_OWNER, false)
requireNotNull(isChatRoomOwner) { "no chat_room_is_owner provided in Intent extras" }
setupToolbar()
......@@ -96,7 +103,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
if (supportFragmentManager.findFragmentByTag(TAG_CHAT_ROOM_FRAGMENT) == null) {
addFragment(TAG_CHAT_ROOM_FRAGMENT, R.id.fragment_container) {
newInstance(chatRoomId, chatRoomName, chatRoomType, isChatRoomReadOnly, chatRoomLastSeen,
isChatRoomSubscribed)
isChatRoomSubscribed, isChatRoomOwner)
}
}
}
......
......@@ -47,7 +47,8 @@ fun newInstance(
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isSubscribed: Boolean = true
isSubscribed: Boolean = true,
isChatRoomOwner: Boolean = false
): Fragment {
return ChatRoomFragment().apply {
arguments = Bundle(1).apply {
......@@ -57,6 +58,7 @@ fun newInstance(
putBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY, isChatRoomReadOnly)
putLong(BUNDLE_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_CHAT_ROOM_IS_OWNER, isChatRoomOwner)
}
}
}
......@@ -68,6 +70,7 @@ private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 42
private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed"
private const val BUNDLE_CHAT_ROOM_IS_OWNER = "chat_room_is_owner"
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener {
@Inject
......@@ -80,6 +83,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private lateinit var chatRoomType: String
private var isSubscribed: Boolean = true
private var isChatRoomReadOnly: Boolean = false
private var isChatRoomOwner: Boolean = false
private lateinit var emojiKeyboardPopup: EmojiKeyboardPopup
private var chatRoomLastSeen: Long = -1
private lateinit var actionSnackbar: ActionSnackbar
......@@ -109,6 +113,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
isChatRoomReadOnly = bundle.getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY)
isSubscribed = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED)
chatRoomLastSeen = bundle.getLong(BUNDLE_CHAT_ROOM_LAST_SEEN)
isChatRoomOwner = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_OWNER)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
......@@ -127,11 +133,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
super.onViewCreated(view, savedInstanceState)
setupToolbar(chatRoomName)
presenter.setupChatRoom()
presenter.loadMessages(chatRoomId, chatRoomType)
presenter.loadChatRooms()
setupRecyclerView()
setupFab()
setupMessageComposer()
setupSuggestionsView()
setupActionSnackbar()
activity?.apply {
......@@ -230,6 +236,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override fun onRoomChanged(canPost: Boolean) {
setupMessageComposer(isChatRoomOwner || canPost)
}
private fun toggleNoChatView(size: Int) {
if (size == 0){
image_chat_icon.setVisible(true)
......@@ -520,12 +530,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override fun onJoined() {
override fun onJoined(canPost: Boolean) {
ui {
input_container.setVisible(true)
button_join_chat.setVisible(false)
isSubscribed = true
setupMessageComposer()
setupMessageComposer(isChatRoomOwner)
}
}
......@@ -556,8 +566,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private fun setupMessageComposer() {
if (isChatRoomReadOnly) {
private fun setupMessageComposer(canPost: Boolean) {
if (!canPost && isChatRoomReadOnly) {
text_room_is_read_only.setVisible(true)
input_container.setVisible(false)
} else if (!isSubscribed) {
......
......@@ -313,6 +313,7 @@ class ViewModelMapper @Inject constructor(
}
private fun getSystemMessage(message: Message): CharSequence {
println(message)
val content = when (message.type) {
//TODO: Add implementation for Welcome type.
is MessageType.MessageRemoved -> context.getString(R.string.message_removed)
......@@ -322,6 +323,8 @@ class ViewModelMapper @Inject constructor(
is MessageType.RoomNameChanged -> context.getString(R.string.message_room_name_changed, message.message, message.sender?.username)
is MessageType.UserRemoved -> context.getString(R.string.message_user_removed_by, message.message, message.sender?.username)
is MessageType.MessagePinned -> context.getString(R.string.message_pinned)
is MessageType.UserMuted -> context.getString(R.string.message_muted, message.message, message.sender?.username)
is MessageType.UserUnMuted -> context.getString(R.string.message_unmuted, message.message, message.sender?.username)
else -> {
throw InvalidParameterException("Invalid message type: ${message.type}")
}
......
package chat.rocket.android.chatrooms.presentation
import chat.rocket.android.R
import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.ChatRoomsSortOrder
import chat.rocket.android.helper.Constants
import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManager
......@@ -23,6 +26,8 @@ import chat.rocket.core.internal.model.Subscription
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.socket.model.StreamMessage
import chat.rocket.core.internal.realtime.socket.model.Type
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.permissions
import chat.rocket.core.internal.rest.spotlight
import chat.rocket.core.model.ChatRoom
import chat.rocket.core.model.Room
......@@ -45,6 +50,9 @@ class ChatRoomsPresenter @Inject constructor(
private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val viewModelMapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor,
private val permissionsInteractor: PermissionsInteractor,
private val localRepository: LocalRepository,
private val userHelper: UserHelper,
settingsRepository: SettingsRepository,
factory: ConnectionManagerFactory
) {
......@@ -69,6 +77,8 @@ class ChatRoomsPresenter @Inject constructor(
refreshSettingsInteractor.refresh(currentServer)
}
view.updateChatRooms(getUserChatRooms())
val permissions = retryIO { client.permissions() }
permissionsInteractor.saveAll(permissions)
} catch (ex: RocketChatException) {
ex.message?.let {
view.showMessage(it)
......@@ -85,7 +95,8 @@ class ChatRoomsPresenter @Inject constructor(
}
fun loadChatRoom(chatRoom: ChatRoom) {
val roomName = if (chatRoom.type is RoomType.DirectMessage
val isDirectMessage = chatRoom.type is RoomType.DirectMessage
val roomName = if (isDirectMessage
&& chatRoom.fullName != null
&& settings.useRealName()) {
chatRoom.fullName!!
......@@ -93,10 +104,40 @@ class ChatRoomsPresenter @Inject constructor(
chatRoom.name
}
navigator.toChatRoom(chatRoom.id, roomName,
chatRoom.type.toString(), chatRoom.readonly ?: false,
chatRoom.lastSeen ?: -1,
chatRoom.open)
launchUI(strategy) {
val myself = getCurrentUser()
if (myself?.username == null) {
view.showMessage(R.string.msg_generic_error)
} else {
val isChatRoomOwner = chatRoom.user?.username == myself.username || isDirectMessage
navigator.toChatRoom(chatRoom.id, roomName,
chatRoom.type.toString(), chatRoom.readonly ?: false,
chatRoom.lastSeen ?: -1,
chatRoom.open, isChatRoomOwner)
}
}
}
private suspend fun getCurrentUser(): User? {
userHelper.user()?.let {
return it
}
try {
val myself = retryIO { client.me() }
val user = User(
id = myself.id,
username = myself.username,
name = myself.name,
status = myself.status,
utcOffset = myself.utcOffset,
emails = null,
roles = myself.roles
)
localRepository.saveCurrentUser(url = currentServer, user = user)
} catch (ex: RocketChatException) {
Timber.e(ex)
}
return null
}
/**
......@@ -415,7 +456,7 @@ class ChatRoomsPresenter @Inject constructor(
val newRoom = ChatRoom(
id = room.id,
type = room.type,
user = room.user ?: user,
user = room.user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
room.name ?: name
......@@ -454,7 +495,7 @@ class ChatRoomsPresenter @Inject constructor(
val newRoom = ChatRoom(
id = subscription.roomId,
type = subscription.type,
user = subscription.user ?: user,
user = user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
subscription.name
......
......@@ -25,10 +25,13 @@ import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
class ChatRoomsAdapter(private val context: Context,
private val settings: PublicSettings,
private val localRepository: LocalRepository,
private val listener: (ChatRoom) -> Unit) : RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder>() {
class ChatRoomsAdapter(
private val context: Context,
private val settings: PublicSettings,
private val localRepository: LocalRepository,
private val listener: (ChatRoom) -> Unit
) : RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder>() {
var dataSet: MutableList<ChatRoom> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_chat))
......
......@@ -23,7 +23,6 @@ import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.showLastMessage
import chat.rocket.android.util.extensions.*
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.common.model.RoomType
......@@ -37,10 +36,14 @@ import timber.log.Timber
import javax.inject.Inject
class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject lateinit var presenter: ChatRoomsPresenter
@Inject lateinit var serverInteractor: GetCurrentServerInteractor
@Inject lateinit var settingsRepository: SettingsRepository
@Inject lateinit var localRepository: LocalRepository
@Inject
lateinit var presenter: ChatRoomsPresenter
@Inject
lateinit var serverInteractor: GetCurrentServerInteractor
@Inject
lateinit var settingsRepository: SettingsRepository
@Inject
lateinit var localRepository: LocalRepository
private lateinit var preferences: SharedPreferences
private var searchView: SearchView? = null
private val handler = Handler()
......@@ -136,9 +139,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
})
val dialogSort = AlertDialog.Builder(context)
.setTitle(R.string.dialog_sort_title)
.setView(dialogLayout)
.setPositiveButton("Done", { dialog, _ -> dialog.dismiss() })
.setTitle(R.string.dialog_sort_title)
.setView(dialogLayout)
.setPositiveButton("Done", { dialog, _ -> dialog.dismiss() })
dialogSort.show()
}
......@@ -146,9 +149,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
return super.onOptionsItemSelected(item)
}
private fun invalidateQueryOnSearch(){
private fun invalidateQueryOnSearch() {
searchView?.let {
if (!searchView!!.isIconified){
if (!searchView!!.isIconified) {
queryChatRoomsByName(searchView!!.query.toString())
}
}
......@@ -163,7 +166,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
/*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await()*/
if (newDataSet.isEmpty()) {
text_no_search.visibility = View.VISIBLE
}else{
text_no_search.visibility = View.GONE
}
if (isActive) {
adapter.baseAdapter.updateRooms(newDataSet)
// TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
......@@ -179,7 +186,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
ui { text_no_data_to_display.setVisible(true) }
}
override fun showLoading(){
override fun showLoading() {
ui { view_loading.setVisible(true) }
}
......@@ -236,19 +243,18 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
ui {
recycler_view.layoutManager = LinearLayoutManager(it, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(it,
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)))
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)))
recycler_view.itemAnimator = DefaultItemAnimator()
// TODO - use a ViewModel Mapper instead of using settings on the adapter
println(serverInteractor.get() + " -> ${settingsRepository.get(serverInteractor.get()!!).showLastMessage()}")
val baseAdapter = ChatRoomsAdapter(it,
settingsRepository.get(serverInteractor.get()!!), localRepository) {
chatRoom -> presenter.loadChatRoom(chatRoom)
settingsRepository.get(serverInteractor.get()!!), localRepository) { chatRoom ->
presenter.loadChatRoom(chatRoom)
}
sectionedAdapter = SimpleSectionedRecyclerViewAdapter(it,
R.layout.item_chatroom_header, R.id.text_chatroom_header, baseAdapter)
R.layout.item_chatroom_header, R.id.text_chatroom_header, baseAdapter)
recycler_view.adapter = sectionedAdapter
}
}
......
......@@ -14,12 +14,10 @@ import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.qualifier.ForFresco
import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.FrescoAuthInterceptor
import chat.rocket.android.helper.MessageParser
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository
import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.*
......@@ -43,7 +41,6 @@ import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import ru.noties.markwon.SpannableConfiguration
......@@ -111,32 +108,16 @@ class AppModule {
@Singleton
fun provideOkHttpClient(logger: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(logger)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build()
}
@Provides
@ForFresco
@Singleton
fun provideFrescoAuthInterceptor(tokenRepository: TokenRepository, currentServerInteractor: GetCurrentServerInteractor): Interceptor {
return FrescoAuthInterceptor(tokenRepository, currentServerInteractor)
}
@Provides
@ForFresco
@Singleton
fun provideFrescoOkHttpClient(okHttpClient: OkHttpClient, @ForFresco authInterceptor: Interceptor): OkHttpClient {
return okHttpClient.newBuilder().apply {
//addInterceptor(authInterceptor)
}.build()
.addInterceptor(logger)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideImagePipelineConfig(context: Context, @ForFresco okHttpClient: OkHttpClient): ImagePipelineConfig {
fun provideImagePipelineConfig(context: Context, okHttpClient: OkHttpClient): ImagePipelineConfig {
val listeners = setOf(RequestLoggingListener())
return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient)
......@@ -177,8 +158,8 @@ class AppModule {
@Provides
@Singleton
fun provideLocalRepository(prefs: SharedPreferences): LocalRepository {
return SharedPrefsLocalRepository(prefs)
fun provideLocalRepository(prefs: SharedPreferences, moshi: Moshi): LocalRepository {
return SharedPreferencesLocalRepository(prefs, moshi)
}
@Provides
......@@ -193,6 +174,12 @@ class AppModule {
return SharedPreferencesSettingsRepository(localRepository)
}
@Provides
@Singleton
fun providePermissionsRepository(localRepository: LocalRepository, moshi: Moshi): PermissionsRepository {
return SharedPreferencesPermissionsRepository(localRepository, moshi)
}
@Provides
@Singleton
fun provideRoomRepository(): RoomRepository {
......@@ -258,7 +245,7 @@ class AppModule {
@Provides
@Singleton
fun provideConfiguration(context: Application, client: OkHttpClient): SpannableConfiguration {
fun provideConfiguration(context: Application): SpannableConfiguration {
val res = context.resources
return SpannableConfiguration.builder(context)
.theme(SpannableTheme.builder()
......@@ -273,12 +260,6 @@ class AppModule {
return MessageParser(context, configuration, settingsInteractor.get(url))
}
@Provides
@Singleton
fun providePermissionInteractor(settingsRepository: SettingsRepository, serverRepository: CurrentServerRepository): GetPermissionsInteractor {
return GetPermissionsInteractor(settingsRepository, serverRepository)
}
@Provides
@Singleton
fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository =
......
......@@ -3,7 +3,21 @@ package chat.rocket.android.dagger.module
import android.content.Context
import android.content.SharedPreferences
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
import chat.rocket.common.internal.ISO8601Date
import chat.rocket.common.model.TimestampAdapter
import chat.rocket.common.util.CalendarISO8601Converter
import chat.rocket.common.util.Logger
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.internal.AttachmentAdapterFactory
import chat.rocket.core.internal.ReactionsAdapter
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
......@@ -11,14 +25,53 @@ import javax.inject.Singleton
@Module
class LocalModule {
@Provides
@Singleton
fun providePlatformLogger(): PlatformLogger {
return TimberLogger
}
@Provides
@Singleton
fun provideCurrentServerRepository(prefs: SharedPreferences): CurrentServerRepository {
return SharedPrefsCurrentServerRepository(prefs)
}
@Provides
@Singleton
fun provideMoshi(
logger: PlatformLogger,
currentServerInteractor: GetCurrentServerInteractor
): Moshi {
val url = currentServerInteractor.get() ?: ""
return Moshi.Builder()
.add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY)
.add(AppJsonAdapterFactory.INSTANCE)
.add(AttachmentAdapterFactory(Logger(logger, url)))
.add(
java.lang.Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(
Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(ReactionsAdapter())
.build()
}
@Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences("rocket.chat", Context.MODE_PRIVATE)
}
@Provides
@Singleton
fun provideLocalRepository(prefs: SharedPreferences): LocalRepository {
return SharedPrefsLocalRepository(prefs)
fun provideLocalRepository(sharedPreferences: SharedPreferences, moshi: Moshi): LocalRepository {
return SharedPreferencesLocalRepository(sharedPreferences, moshi)
}
}
\ No newline at end of file
package chat.rocket.android.dagger.qualifier
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ForFresco
\ No newline at end of file
package chat.rocket.android.helper
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import okhttp3.Interceptor
import okhttp3.Response
class FrescoAuthInterceptor(
private val tokenRepository: TokenRepository,
private val currentServerInteractor: GetCurrentServerInteractor
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
currentServerInteractor.get()?.let { serverUrl ->
val token = tokenRepository.get(serverUrl)
return@let token?.let {
val url = request.url().newBuilder().apply {
addQueryParameter("rc_uid", token.userId)
addQueryParameter("rc_token", token.authToken)
}.build()
request = request.newBuilder().apply {
url(url)
}.build()
}
}
return chain.proceed(request)
}
}
\ No newline at end of file
package chat.rocket.android.helper
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.SettingsRepository
import chat.rocket.android.server.domain.useRealName
import chat.rocket.common.model.User
import javax.inject.Inject
class UserHelper @Inject constructor(
private val localRepository: LocalRepository,
private val getCurrentServerInteractor: GetCurrentServerInteractor,
settingsRepository: SettingsRepository
) {
private val settings: PublicSettings = settingsRepository.get(getCurrentServerInteractor.get()!!)
/**
* Return the display name for the given [user].
* If setting 'Use_Real_Name' is true then the real name will be given, or else
* the username without the '@' is yielded. The fallback for any case is the username, which
* could be null.
*/
fun displayName(user: User): String? {
return if (settings.useRealName()) user.name ?: user.username else user.username
}
/**
* Return current logged user's display name.
*
* @see displayName
*/
fun displayName(): String? {
user()?.let {
return displayName(it)
}
return null
}
/**
* Return current logged [User].
*/
fun user(): User? {
return localRepository.getCurrentUser(serverUrl())
}
/**
* Return the username for the current logged [User].
*/
fun username(): String? = user()?.username
/**
* Whether current [User] is admin on the current server.
*/
fun isAdmin(): Boolean {
return user()?.roles?.find { it.equals("admin", ignoreCase = true) } != null
}
private fun serverUrl(): String {
return getCurrentServerInteractor.get()!!
}
}
\ No newline at end of file
package chat.rocket.android.infrastructure
import chat.rocket.common.model.User
interface LocalRepository {
fun save(key: String, value: String?)
......@@ -14,12 +16,16 @@ interface LocalRepository {
fun getLong(key: String, defValue: Long = -1L): Long
fun clear(key: String)
fun clearAllFromServer(server: String)
fun getCurrentUser(url: String): User?
fun saveCurrentUser(url: String, user: User)
companion object {
const val KEY_PUSH_TOKEN = "KEY_PUSH_TOKEN"
const val MIGRATION_FINISHED_KEY = "MIGRATION_FINISHED_KEY"
const val TOKEN_KEY = "token_"
const val SETTINGS_KEY = "settings_"
const val PERMISSIONS_KEY = "permissions_"
const val USER_KEY = "user_"
const val CURRENT_USERNAME_KEY = "username_"
}
}
......
......@@ -2,8 +2,26 @@ package chat.rocket.android.infrastructure
import android.content.SharedPreferences
import androidx.core.content.edit
import chat.rocket.common.model.User
import com.squareup.moshi.Moshi
class SharedPreferencesLocalRepository(
private val preferences: SharedPreferences,
moshi: Moshi
) : LocalRepository {
private val userAdapter = moshi.adapter(User::class.java)
override fun getCurrentUser(url: String): User? {
return get("${url}_${LocalRepository.USER_KEY}", null)?.let {
userAdapter.fromJson(it)
}
}
override fun saveCurrentUser(url: String, user: User) {
save("${url}_${LocalRepository.USER_KEY}", userAdapter.toJson(user))
}
class SharedPrefsLocalRepository(private val preferences: SharedPreferences) : LocalRepository {
override fun getBoolean(key: String, defValue: Boolean) = preferences.getBoolean(key, defValue)
override fun getFloat(key: String, defValue: Float) = preferences.getFloat(key, defValue)
......
......@@ -36,9 +36,10 @@ class MainNavigator(internal val activity: MainActivity) {
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean) {
isChatRoomSubscribed: Boolean,
isChatRoomOwner: Boolean) {
activity.startActivity(activity.chatRoomIntent(chatRoomId, chatRoomName, chatRoomType,
isChatRoomReadOnly, chatRoomLastSeen, isChatRoomSubscribed))
isChatRoomReadOnly, chatRoomLastSeen, isChatRoomSubscribed, isChatRoomOwner))
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
}
......
package chat.rocket.android.server.domain
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.core.model.Permission
import javax.inject.Inject
class GetPermissionsInteractor @Inject constructor(private val settingsRepository: SettingsRepository,
private val currentServerRepository: CurrentServerRepository) {
// Creating rooms
const val CREATE_PUBLIC_CHANNELS = "create-c"
const val CREATE_DIRECT_MESSAGES = "create-d"
const val CREATE_PRIVATE_CHANNELS = "create-p"
private fun publicSettings(): PublicSettings? = settingsRepository.get(currentServerRepository.get()!!)
// Messages
const val DELETE_MESSAGE = "delete-message"
const val FORCE_DELETE_MESSAGE = "force-delete-message"
const val EDIT_MESSAGE = "edit-message"
const val PIN_MESSAGE = "pin-message"
const val POST_READONLY = "post-readonly"
class PermissionsInteractor @Inject constructor(
private val settingsRepository: SettingsRepository,
private val permissionsRepository: PermissionsRepository,
private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val userHelper: UserHelper
) {
private fun publicSettings(): PublicSettings? = settingsRepository.get(currentServerUrl()!!)
fun saveAll(permissions: List<Permission>) {
val url = currentServerUrl()!!
permissions.forEach { permissionsRepository.save(url, it) }
}
/**
* Check whether user is allowed to delete a message.
......@@ -31,4 +55,18 @@ class GetPermissionsInteractor @Inject constructor(private val settingsRepositor
* Checks whether should show edited message status.
*/
fun showEditedStatus() = publicSettings()?.showEditedStatus() ?: false
fun canPostToReadOnlyChannels(): Boolean {
val url = getCurrentServerInteractor.get()!!
val currentUserRoles = userHelper.user()?.roles
return permissionsRepository.get(url, POST_READONLY)?.let { permission ->
currentUserRoles?.isNotEmpty() == true && permission.roles.any {
currentUserRoles.contains(it)
}
} == true || userHelper.isAdmin()
}
private fun currentServerUrl(): String? {
return getCurrentServerInteractor.get()
}
}
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.core.model.Permission
interface PermissionsRepository {
/**
* Store [permission] locally.
*
* @param url The server url from where we're interest to store the permission.
* @param permission The permission to store.
*/
fun save(url: String, permission: Permission)
/**
* Get permission given by the [permissionId] and for the server [url].
*
* @param url The server url from where we're interested on getting the permissions.
* @param permissionId the id of the permission to get.
*
* @return The interested [Permission] or null if not found.
*/
fun get(url: String, permissionId: String): Permission?
}
\ No newline at end of file
package chat.rocket.android.server.infraestructure
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.PermissionsRepository
import chat.rocket.core.model.Permission
import com.squareup.moshi.Moshi
class SharedPreferencesPermissionsRepository(
private val localRepository: LocalRepository,
moshi: Moshi
) : PermissionsRepository {
private val adapter = moshi.adapter(Permission::class.java)
override fun save(url: String, permission: Permission) {
localRepository.save(getPermissionKey(url, permission.id), adapter.toJson(permission))
}
override fun get(url: String, permissionId: String): Permission? {
return localRepository.get(getPermissionKey(url, permissionId))?.let {
adapter.fromJson(it)
}
}
// Create a key following the pattern: settings_[url]_[permission id]
// eg.: 'settings_https://open.rocket.chat_create-p'
private fun getPermissionKey(url: String, permissionId: String): String {
return "${LocalRepository.PERMISSIONS_KEY}${url}_$permissionId"
}
}
\ No newline at end of file
......@@ -6,7 +6,9 @@ import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.core.internal.SettingsAdapter
class SharedPreferencesSettingsRepository(private val localRepository: LocalRepository) : SettingsRepository {
class SharedPreferencesSettingsRepository(
private val localRepository: LocalRepository
) : SettingsRepository {
private val adapter = SettingsAdapter().lenient()
......
......@@ -45,4 +45,15 @@
tools:text="connected"
tools:visibility="visible" />
<TextView
android:id="@+id/text_no_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="56dp"
android:text="@string/msg_no_search_found"
android:textSize="20sp"
android:layout_centerHorizontal="true"
android:visibility="gone"
tools:visibility="visible" />
</RelativeLayout>
\ No newline at end of file
......@@ -111,6 +111,7 @@
<string name="msg_no_chat_title">Sin mensajes de chat</string>
<string name="msg_no_chat_description">Comience a conversar para ver\nsus mensajes aquí.</string>
<string name="msg_edited">(editado)</string>
<string name="msg_no_search_found">No se han encontrado resultados</string>
<!-- System messages -->
<string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string>
......@@ -121,6 +122,8 @@
<string name="message_welcome">Bienvenido %s</string>
<string name="message_removed">Mensaje eliminado</string>
<string name="message_pinned">Fijado una mensaje:</string>
<string name="message_muted">Usuario %1$s silenciado por %2$s</string>
<string name="message_unmuted">Usuario %1$s no silenciado por %2$s</string>
<!-- Message actions -->
<string name="action_msg_reply">Respuesta</string>
......
......@@ -111,6 +111,7 @@
<string name="msg_no_chat_title">Aucun message de discussion</string>
<string name="msg_no_chat_description">Commencez à converser pour voir\nvos messages ici.</string>
<string name="msg_edited">(édité)</string>
<string name="msg_no_search_found">Aucun résultat trouvé</string>
<!-- System messages -->
<string name="message_room_name_changed">Le nom de le salle a changé à: %1$s par %2$s</string>
......@@ -121,6 +122,8 @@
<string name="message_welcome">Bienvenue %s</string>
<string name="message_removed">Message supprimé</string>
<string name="message_pinned">Épinglé un message:</string>
<string name="message_muted">Utilisateur %1$s mis en sourdine par %2$s</string>
<string name="message_unmuted">Utilisateur %1$s non muté par %2$s</string>
<!-- Message actions -->
<string name="action_msg_reply">Répondre</string>
......
......@@ -113,6 +113,7 @@
<string name="msg_no_chat_title">कोई चैट संदेश नहीं</string>
<string name="msg_no_chat_description">यहां अपने संदेश देखने के लिए\nबातचीत शुरू करें।</string>
<string name="msg_edited">(संपादित)</string>
<string name="msg_no_search_found">कोई परिणाम नहीं मिला</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
......@@ -123,6 +124,8 @@
<string name="message_welcome">%s का स्वागत करते हैं</string>
<string name="message_removed">संदेश हटाया गया</string>
<string name="message_pinned">एक संदेश पिन किया:</string>
<string name="message_muted">उपयोगकर्ता %1$s %2$s द्वारा म्यूट किया गया</string>
<string name="message_unmuted">उपयोगकर्ता %1$s %2$s द्वारा अनम्यूट किया गया</string>
<!-- Message actions -->
<string name="action_msg_reply">जवाब दें</string>
......
......@@ -107,16 +107,19 @@
<string name="msg_image_saved_successfully">Imagem salva na galeria</string>
<string name="msg_image_saved_failed">Falha ao salvar a imagem</string>
<string name="msg_edited">(editado)</string>
<string name="msg_no_search_found">nenhum resultado encontrado</string>
<!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
<string name="message_user_removed_by">Usuário %1$s removido por %2$s</string>
<string name="message_user_left">Saiu da sala.</string>
<string name="message_user_joined_channel">Entrou na sala.</string>
<string name="message_user_left">Saiu da sala</string>
<string name="message_user_joined_channel">Entrou na sala</string>
<string name="message_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</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_unmuted">Usuário %1$s saiu do modo mudo por %2$s</string>
<!-- Message actions -->
<string name="action_msg_reply">Responder</string>
......@@ -124,11 +127,11 @@
<string name="action_msg_copy">Copiar</string>
<string name="action_msg_quote">Citar</string>
<string name="action_msg_delete">Remover</string>
<string name="action_msg_pin">Fixar Mensagem</string>
<string name="action_msg_unpin">Desafixar Mensagem</string>
<string name="action_msg_star">Favoritar Mensagem</string>
<string name="action_msg_pin">Pinar mensagem</string>
<string name="action_msg_unpin">Despinar mensagem</string>
<string name="action_msg_star">Favoritar mensagem</string>
<string name="action_msg_share">Compartilhar</string>
<string name="action_title_editing">Editando Mensagem</string>
<string name="action_title_editing">Editando mensagem</string>
<string name="action_msg_add_reaction">Adicionar reação</string>
<!-- Permission messages -->
......@@ -149,7 +152,7 @@
<!-- Socket status -->
<string name="status_connected">Conectado</string>
<string name="status_disconnected">Desconetado</string>
<string name="status_disconnected">Desconectado</string>
<string name="status_connecting">Conectando</string>
<string name="status_authenticating">Autenticando</string>
<string name="status_disconnecting">Desconectando</string>
......@@ -166,7 +169,7 @@
<string name="Slash_Tableflip_Description">Exibir (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Exibir ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Criar um novo canal</string>
<string name="Show_the_keyboard_shortcut_list">Show the keyboard shortcut list</string>
<string name="Show_the_keyboard_shortcut_list">Exibir a lista de atalhos do teclado</string>
<string name="Invite_user_to_join_channel_all_from"> do [#canal] para entrar neste</string>
<string name="Invite_user_to_join_channel_all_to">Convidar todos os usuários deste canal para entrar no [#canal]</string>
<string name="Archive">Arquivar</string>
......@@ -174,8 +177,8 @@
<string name="Leave_the_current_channel">Sair do canal atual</string>
<string name="Displays_action_text">Exibir texto de ação</string>
<string name="Direct_message_someone">Enviar DM para alguém</string>
<string name="Mute_someone_in_room">Silenciar alguém</string>
<string name="Unmute_someone_in_room">De-silenciar alguém na sala</string>
<string name="Mute_someone_in_room">Ativar o modo mudo em alguém</string>
<string name="Unmute_someone_in_room">Desativar o modo mudo em alguém na sala</string>
<string name="Invite_user_to_join_channel">Convidar algum usuário para entrar neste canal</string>
<string name="Unarchive">Desarquivar</string>
<string name="Join_the_given_channel">Entrar no canal especificado</string>
......
......@@ -108,6 +108,7 @@
<string name="msg_image_saved_successfully">Image has been saved to gallery</string>
<string name="msg_image_saved_failed">Failed to save image</string>
<string name="msg_edited">(edited)</string>
<string name="msg_no_search_found">No result found</string>
<!-- System messages -->
<string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string>
......@@ -118,6 +119,8 @@
<string name="message_welcome">Welcome %s</string>
<string name="message_removed">Message removed</string>
<string name="message_pinned">Pinned a message:</string>
<string name="message_muted">User %1$s muted by %2$s</string>
<string name="message_unmuted">User %1$s unmuted by %2$s</string>
<!-- Message actions -->
<string name="action_msg_reply">Reply</string>
......
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