diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt index 42d221e6..76ca6d6f 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VkConversation.kt @@ -10,27 +10,28 @@ import kotlinx.parcelize.Parcelize @Parcelize data class VkConversation( @PrimaryKey(autoGenerate = false) - val id: Int, - val ownerId: Int?, - val title: String?, - val photo200: String?, - val type: String, - val callInProgress: Boolean, - val isPhantom: Boolean, - val lastConversationMessageId: Int, - val inRead: Int, - val outRead: Int, - val isMarkedUnread: Boolean, - val lastMessageId: Int, - val unreadCount: Int?, - val membersCount: Int?, - val isPinned: Boolean, + var id: Int, + var ownerId: Int?, + var title: String?, + var photo200: String?, + var type: String, + var callInProgress: Boolean, + var isPhantom: Boolean, + var lastConversationMessageId: Int, + var inRead: Int, + var outRead: Int, + var isMarkedUnread: Boolean, + var lastMessageId: Int, + var unreadCount: Int?, + var membersCount: Int?, + var isPinned: Boolean, + var canChangePin: Boolean, @Embedded(prefix = "pinnedMessage_") var pinnedMessage: VkMessage? = null, @Embedded(prefix = "lastMessage_") - var lastMessage: VkMessage? = null + var lastMessage: VkMessage? = null, ) : Parcelable { fun isChat() = type == "chat" diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt index cd0beaca..424c43e1 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt @@ -4,8 +4,10 @@ import androidx.lifecycle.MutableLiveData import androidx.room.Entity import androidx.room.Ignore import androidx.room.PrimaryKey +import com.meloda.fast.api.UserConfig import com.meloda.fast.api.model.attachments.VkAttachment import com.meloda.fast.base.adapter.SelectableItem +import com.meloda.fast.util.TimeUtils import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @@ -58,6 +60,10 @@ data class VkMessage( return Action.parse(action) } + fun canEdit() = + fromId == UserConfig.userId && + (System.currentTimeMillis() / 1000 - date.toLong() < TimeUtils.ONE_DAY_IN_SECONDS) + fun copyMessage( id: Int = this.id, text: String? = this.text, diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt index 25490fa1..8c7177f2 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkConversation.kt @@ -40,7 +40,8 @@ data class BaseVkConversation( unreadCount = unread_count, membersCount = chat_settings?.members_count, ownerId = chat_settings?.owner_id, - isPinned = sort_id.major_id > 0 + isPinned = sort_id.major_id > 0, + canChangePin = chat_settings?.acl?.can_change_pin == true ).apply { this.lastMessage = lastMessage this.pinnedMessage = chat_settings?.pinned_message?.asVkMessage() diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/request/MessagesRequest.kt b/app/src/main/kotlin/com/meloda/fast/api/model/request/MessagesRequest.kt index fda2f97f..1cb01f21 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/model/request/MessagesRequest.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/request/MessagesRequest.kt @@ -70,6 +70,28 @@ data class MessagesMarkAsImportantRequest( } +@Parcelize +data class MessagesPinMessageRequest( + val peerId: Int, + val messageId: Int? = null, + val conversationMessageId: Int? = null +) : Parcelable { + + val map + get() = mutableMapOf( + "peer_id" to peerId.toString() + ).apply { + messageId?.let { this["message_id"] = it.toString() } + conversationMessageId?.let { this["conversation_message_id"] = it.toString() } + } + +} + +@Parcelize +data class MessagesUnPinMessageRequest(val peerId: Int) : Parcelable { + val map get() = mutableMapOf("peer_id" to peerId.toString()) +} + @Parcelize data class MessagesGetLongPollServerRequest( val needPts: Boolean, diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/VkUrls.kt similarity index 87% rename from app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt rename to app/src/main/kotlin/com/meloda/fast/api/network/VkUrls.kt index d0476e13..ea4f2c5f 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/network/VKUrls.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/network/VkUrls.kt @@ -1,6 +1,6 @@ package com.meloda.fast.api.network -object VKUrls { +object VkUrls { const val OAUTH = "https://oauth.vk.com" const val API = "https://api.vk.com/method" @@ -22,6 +22,8 @@ object VKUrls { const val GetHistory = "$API/messages.getHistory" const val Send = "$API/messages.send" const val MarkAsImportant = "$API/messages.markAsImportant" + const val Pin = "$API/messages.pin" + const val Unpin = "$API/messages.unpin" const val GetLongPollServer = "$API/messages.getLongPollServer" const val GetLongPollHistory = "$API/messages.getLongPollHistory" } diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/datasource/MessagesDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/network/datasource/MessagesDataSource.kt index 5995d515..472004da 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/network/datasource/MessagesDataSource.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/network/datasource/MessagesDataSource.kt @@ -1,10 +1,7 @@ package com.meloda.fast.api.network.datasource import com.meloda.fast.api.model.VkMessage -import com.meloda.fast.api.model.request.MessagesGetHistoryRequest -import com.meloda.fast.api.model.request.MessagesGetLongPollServerRequest -import com.meloda.fast.api.model.request.MessagesMarkAsImportantRequest -import com.meloda.fast.api.model.request.MessagesSendRequest +import com.meloda.fast.api.model.request.* import com.meloda.fast.api.network.repo.MessagesRepo import com.meloda.fast.database.dao.MessagesDao import javax.inject.Inject @@ -26,8 +23,14 @@ class MessagesDataSource @Inject constructor( suspend fun getLongPollServer(params: MessagesGetLongPollServerRequest) = repo.getLongPollServer(params.map) - suspend fun storeMessages(messages: List) = dao.insert(messages) + suspend fun pin(params: MessagesPinMessageRequest) = + repo.pin(params.map) - suspend fun getCachedMessages(peerId: Int) = dao.getByPeerId(peerId) + suspend fun unpin(params: MessagesUnPinMessageRequest) = + repo.unpin(params.map) + + suspend fun store(messages: List) = dao.insert(messages) + + suspend fun getCached(peerId: Int) = dao.getByPeerId(peerId) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt index 60fed0f8..e0b72cba 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/AuthRepo.kt @@ -1,6 +1,6 @@ package com.meloda.fast.api.network.repo -import com.meloda.fast.api.network.VKUrls +import com.meloda.fast.api.network.VkUrls import com.meloda.fast.api.model.response.ResponseAuthDirect import com.meloda.fast.api.network.Answer import com.meloda.fast.api.model.response.ResponseSendSms @@ -8,10 +8,10 @@ import retrofit2.http.* interface AuthRepo { - @GET(VKUrls.Auth.DirectAuth) + @GET(VkUrls.Auth.DirectAuth) suspend fun auth(@QueryMap param: Map): Answer - @GET(VKUrls.Auth.SendSms) + @GET(VkUrls.Auth.SendSms) suspend fun sendSms(@Query("sid") validationSid: String): Answer } \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt index 9f0c8d4c..b394e85a 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/ConversationsRepo.kt @@ -2,7 +2,7 @@ package com.meloda.fast.api.network.repo import com.meloda.fast.api.base.ApiResponse import com.meloda.fast.api.network.Answer -import com.meloda.fast.api.network.VKUrls +import com.meloda.fast.api.network.VkUrls import com.meloda.fast.api.model.response.ConversationsGetResponse import retrofit2.http.FieldMap import retrofit2.http.FormUrlEncoded @@ -11,7 +11,7 @@ import retrofit2.http.POST interface ConversationsRepo { @FormUrlEncoded - @POST(VKUrls.Conversations.Get) + @POST(VkUrls.Conversations.Get) suspend fun getAllChats(@FieldMap params: Map): Answer> } \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt index 831d4c49..1b4c6717 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/MessagesRepo.kt @@ -2,9 +2,10 @@ package com.meloda.fast.api.network.repo import com.meloda.fast.api.base.ApiResponse import com.meloda.fast.api.model.base.BaseVkLongPoll +import com.meloda.fast.api.model.base.BaseVkMessage import com.meloda.fast.api.model.response.MessagesGetHistoryResponse import com.meloda.fast.api.network.Answer -import com.meloda.fast.api.network.VKUrls +import com.meloda.fast.api.network.VkUrls import retrofit2.http.FieldMap import retrofit2.http.FormUrlEncoded import retrofit2.http.POST @@ -12,19 +13,27 @@ import retrofit2.http.POST interface MessagesRepo { @FormUrlEncoded - @POST(VKUrls.Messages.GetHistory) + @POST(VkUrls.Messages.GetHistory) suspend fun getHistory(@FieldMap params: Map): Answer> @FormUrlEncoded - @POST(VKUrls.Messages.Send) + @POST(VkUrls.Messages.Send) suspend fun send(@FieldMap params: Map): Answer> @FormUrlEncoded - @POST(VKUrls.Messages.MarkAsImportant) + @POST(VkUrls.Messages.MarkAsImportant) suspend fun markAsImportant(@FieldMap params: Map): Answer>> @FormUrlEncoded - @POST(VKUrls.Messages.GetLongPollServer) + @POST(VkUrls.Messages.GetLongPollServer) suspend fun getLongPollServer(@FieldMap params: Map): Answer> + @FormUrlEncoded + @POST(VkUrls.Messages.Pin) + suspend fun pin(@FieldMap params: Map): Answer> + + @FormUrlEncoded + @POST(VkUrls.Messages.Unpin) + suspend fun unpin(@FieldMap params: Map): Answer> + } \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt index 782cb32f..f11a408d 100644 --- a/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/network/repo/UsersRepo.kt @@ -3,7 +3,7 @@ package com.meloda.fast.api.network.repo import com.meloda.fast.api.base.ApiResponse import com.meloda.fast.api.model.base.BaseVkUser import com.meloda.fast.api.network.Answer -import com.meloda.fast.api.network.VKUrls +import com.meloda.fast.api.network.VkUrls import retrofit2.http.FieldMap import retrofit2.http.FormUrlEncoded import retrofit2.http.POST @@ -11,7 +11,7 @@ import retrofit2.http.POST interface UsersRepo { @FormUrlEncoded - @POST(VKUrls.Users.GetById) + @POST(VkUrls.Users.GetById) suspend fun getById( @FieldMap params: Map? ): Answer>> diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseViewModelFragment.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseViewModelFragment.kt index cb21a3e1..40ddc1f0 100644 --- a/app/src/main/kotlin/com/meloda/fast/base/BaseViewModelFragment.kt +++ b/app/src/main/kotlin/com/meloda/fast/base/BaseViewModelFragment.kt @@ -11,7 +11,7 @@ import com.meloda.fast.activity.MainActivity import com.meloda.fast.api.UserConfig import com.meloda.fast.base.viewmodel.BaseViewModel import com.meloda.fast.base.viewmodel.IllegalTokenEvent -import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.base.viewmodel.VkEvent import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach @@ -30,7 +30,7 @@ abstract class BaseViewModelFragment : BaseFragment { } } - protected open fun onEvent(event: VKEvent) { + protected open fun onEvent(event: VkEvent) { if (event is IllegalTokenEvent) { Toast.makeText( requireContext(), R.string.authorization_failed, Toast.LENGTH_LONG diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt index 97c3220b..8ce65c7e 100644 --- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt +++ b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt @@ -15,7 +15,7 @@ abstract class BaseViewModel : ViewModel() { var unknownErrorDefaultText: String = "" - protected val tasksEventChannel = Channel() + protected val tasksEventChannel = Channel() val tasksEvent = tasksEventChannel.receiveAsFlow() protected fun makeJob( @@ -25,22 +25,35 @@ abstract class BaseViewModel : ViewModel() { onEnd: (suspend () -> Unit)? = null, onError: (suspend (Throwable) -> Unit)? = null ) = viewModelScope.launch { - onStart?.invoke() + onStart?.invoke() ?: onStart() when (val response = job()) { is Answer.Success -> onAnswer(response.data) is Answer.Error -> { checkErrors(response.throwable) - onError?.invoke(response.throwable) ?: sendEvent( - ErrorEvent( - response.throwable.message - ?: unknownErrorDefaultText - ) - ) + onError?.invoke(response.throwable) ?: onError(response.throwable) } } - }.also { it.invokeOnCompletion { viewModelScope.launch { onEnd?.invoke() } } } + }.also { + it.invokeOnCompletion { + viewModelScope.launch { + onEnd?.invoke() ?: onStop() + } + } + } - protected suspend fun sendEvent(event: T) = tasksEventChannel.send(event) + protected suspend fun onStart() { + sendEvent(StartProgressEvent) + } + + protected suspend fun onStop() { + sendEvent(StopProgressEvent) + } + + protected suspend fun onError(throwable: Throwable) { + sendEvent(ErrorEvent(throwable.message ?: unknownErrorDefaultText)) + } + + protected suspend fun sendEvent(event: T) = tasksEventChannel.send(event) private suspend fun checkErrors(throwable: Throwable) { when (throwable) { diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt index 0bac43e5..52f55dfa 100644 --- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt +++ b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt @@ -5,15 +5,15 @@ data class ShowDialogInfoEvent( val message: String, val positiveBtn: String? = null, val negativeBtn: String? = null -) : VKEvent() +) : VkEvent() -data class ErrorEvent(val errorText: String) : VKEvent() +data class ErrorEvent(val errorText: String) : VkEvent() -object IllegalTokenEvent : VKEvent() -data class CaptchaEvent(val sid: String, val image: String) : VKEvent() -data class ValidationEvent(val sid: String) : VKEvent() +object IllegalTokenEvent : VkEvent() +data class CaptchaEvent(val sid: String, val image: String) : VkEvent() +data class ValidationEvent(val sid: String) : VkEvent() -object StartProgressEvent : VKEvent() -object StopProgressEvent : VKEvent() +object StartProgressEvent : VkEvent() +object StopProgressEvent : VkEvent() -abstract class VKEvent \ No newline at end of file +abstract class VkEvent \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt b/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt index fb47317e..e8d5c859 100644 --- a/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt +++ b/app/src/main/kotlin/com/meloda/fast/database/AppDatabase.kt @@ -19,7 +19,7 @@ import com.meloda.fast.database.dao.UsersDao VkUser::class, VkGroup::class ], - version = 25, + version = 26, exportSchema = false, ) @TypeConverters(Converters::class) diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt index 63bb37a3..0071d184 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsFragment.kt @@ -24,7 +24,7 @@ import com.meloda.fast.api.model.VkConversation import com.meloda.fast.base.BaseViewModelFragment import com.meloda.fast.base.viewmodel.StartProgressEvent import com.meloda.fast.base.viewmodel.StopProgressEvent -import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.base.viewmodel.VkEvent import com.meloda.fast.common.AppGlobal import com.meloda.fast.common.AppSettings import com.meloda.fast.common.dataStore @@ -185,7 +185,7 @@ class ConversationsFragment : .show() } - override fun onEvent(event: VKEvent) { + override fun onEvent(event: VkEvent) { super.onEvent(event) when (event) { is ConversationsLoaded -> refreshConversations(event) diff --git a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt index 9e83ebd7..83e8906b 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/conversations/ConversationsViewModel.kt @@ -11,9 +11,7 @@ import com.meloda.fast.api.model.request.UsersGetRequest import com.meloda.fast.api.network.datasource.ConversationsDataSource import com.meloda.fast.api.network.datasource.UsersDataSource import com.meloda.fast.base.viewmodel.BaseViewModel -import com.meloda.fast.base.viewmodel.StartProgressEvent -import com.meloda.fast.base.viewmodel.StopProgressEvent -import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.base.viewmodel.VkEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -66,13 +64,8 @@ class ConversationsViewModel @Inject constructor( ) ) } - }, - onError = { - val er = it - throw it - }, - onStart = { sendEvent(StartProgressEvent) }, - onEnd = { sendEvent(StopProgressEvent) }) + } + ) } fun loadProfileUser() = viewModelScope.launch { @@ -95,4 +88,4 @@ data class ConversationsLoaded( val conversations: List, val profiles: HashMap, val groups: HashMap -) : VKEvent() +) : VkEvent() diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt index f96a9bcc..ce4b2417 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt @@ -70,7 +70,7 @@ class LoginFragment : BaseViewModelFragment(R.layout.fragment_lo binding.loginInput.clearFocus() } - override fun onEvent(event: VKEvent) { + override fun onEvent(event: VkEvent) { super.onEvent(event) when (event) { diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt index 93530186..330344e9 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginViewModel.kt @@ -51,26 +51,25 @@ class LoginViewModel @Inject constructor( sendEvent(SuccessAuth()) }, onError = { - if (it !is VKException) return@makeJob + if (it !is VKException) { + onError(it) + return@makeJob + } // TODO: 9/27/2021 use `delay` parameter twoFaCode?.let { sendEvent(CodeSent) } - }, - onStart = { sendEvent(StartProgressEvent) }, - onEnd = { sendEvent(StopProgressEvent) } + } ) } fun sendSms(validationSid: String) = viewModelScope.launch { makeJob({ dataSource.sendSms(validationSid) }, - onAnswer = { sendEvent(CodeSent) }, - onError = {}, - onStart = {}, - onEnd = {}) + onAnswer = { sendEvent(CodeSent) } + ) } } -object CodeSent : VKEvent() +object CodeSent : VkEvent() -data class SuccessAuth(val haveAuthorized: Boolean = true) : VKEvent() \ No newline at end of file +data class SuccessAuth(val haveAuthorized: Boolean = true) : VkEvent() \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt index 6f3343eb..5e231f39 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/AttachmentInflater.kt @@ -45,6 +45,13 @@ class AttachmentInflater constructor( private val playColor = ContextCompat.getColor(context, R.color.a3_700) private val playBackgroundColor = ContextCompat.getColor(context, R.color.a3_200) + var photoClickListener: ((url: String) -> Unit)? = null + + fun setPhotoClickListener(unit: ((url: String) -> Unit)?): AttachmentInflater { + this.photoClickListener = unit + return this + } + fun inflate() { if (message.attachments.isNullOrEmpty()) return attachments = message.attachments!! @@ -114,6 +121,12 @@ class AttachmentInflater constructor( scaleType = ImageView.ScaleType.CENTER_CROP } + if (photoClickListener != null) { + newPhoto.setOnClickListener { photoClickListener?.invoke(size.url) } + } else { + newPhoto.setOnClickListener(null) + } + val spacer = Space(context).also { it.layoutParams = LinearLayoutCompat.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt index d8b22578..594ef13a 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryAdapter.kt @@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable import android.view.View import android.view.ViewGroup import android.widget.AdapterView +import android.widget.Toast import androidx.appcompat.widget.LinearLayoutCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil @@ -16,6 +17,7 @@ import com.meloda.fast.api.model.VkConversation import com.meloda.fast.api.model.VkGroup import com.meloda.fast.api.model.VkMessage import com.meloda.fast.api.model.VkUser +import com.meloda.fast.api.model.attachments.VkAttachment import com.meloda.fast.api.model.attachments.VkPhoto import com.meloda.fast.base.adapter.BaseAdapter import com.meloda.fast.base.adapter.BaseHolder @@ -36,6 +38,8 @@ class MessagesHistoryAdapter constructor( var onItemClickListener: ((position: Int, view: View) -> Unit)? = null + var attachmentClickListener: ((attachment: VkAttachment) -> Unit)? = null + override fun getItemViewType(position: Int): Int { when { isPositionHeader(position) -> return HEADER @@ -123,6 +127,8 @@ class MessagesHistoryAdapter constructor( prevMessage = prevMessage, nextMessage = nextMessage, + title = binding.title, + avatar = binding.avatar, bubble = binding.bubble, text = binding.text, @@ -133,7 +139,9 @@ class MessagesHistoryAdapter constructor( profiles = profiles, groups = groups - ).prepare() + ).setPhotoClickListener { + Toast.makeText(context, "Photo url: $it", Toast.LENGTH_LONG).show() + }.prepare() } } diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt index 631589f4..fafca889 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryFragment.kt @@ -27,10 +27,9 @@ import com.meloda.fast.api.model.VkUser import com.meloda.fast.base.BaseViewModelFragment import com.meloda.fast.base.viewmodel.StartProgressEvent import com.meloda.fast.base.viewmodel.StopProgressEvent -import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.base.viewmodel.VkEvent import com.meloda.fast.databinding.FragmentMessagesHistoryBinding import com.meloda.fast.extensions.TextViewExtensions.clear -import com.meloda.fast.extensions.isNotVisible import com.meloda.fast.util.AndroidUtils import com.meloda.fast.util.TimeUtils import dagger.hilt.android.AndroidEntryPoint @@ -49,7 +48,7 @@ class MessagesHistoryFragment : private val action = MutableLiveData() private enum class Action { - RECORD, SEND + RECORD, SEND, EDIT } private val user: VkUser? by lazy { @@ -71,14 +70,15 @@ class MessagesHistoryFragment : } } - private val replyMessage = MutableLiveData() - private val isAttachmentPanelVisible = MutableLiveData(false) - private var timestampTimer: Timer? = null + private lateinit var attachmentController: AttachmentPanelController + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + attachmentController = AttachmentPanelController().init() + val title = when { conversation.isChat() -> conversation.title conversation.isUser() -> user?.toString() @@ -167,8 +167,11 @@ class MessagesHistoryFragment : it.toString().isNotBlank() val newValue = - if (canSend) Action.SEND - else Action.RECORD + when { + attachmentController.isEditing -> Action.EDIT + canSend -> Action.SEND + else -> Action.RECORD + } if (action.value != newValue) action.value = newValue } @@ -193,30 +196,21 @@ class MessagesHistoryFragment : Action.SEND -> { binding.action.setImageResource(R.drawable.ic_round_send_24) } + Action.EDIT -> { + binding.action.setImageResource(R.drawable.ic_round_done_24) + } else -> return@observe } } - isAttachmentPanelVisible.observe(viewLifecycleOwner) { + attachmentController.isPanelVisible.observe(viewLifecycleOwner) { val layoutParams = binding.refreshLayout.layoutParams as CoordinatorLayout.LayoutParams layoutParams.bottomMargin = if (it) (binding.attachmentPanel.height / 1.5).roundToInt() else 0 } - hideAttachmentPanel(duration = 1) - - binding.avatar.setOnClickListener { - val isShown = binding.attachmentPanel.isVisible - - if (isShown) { - hideAttachmentPanel() - } else { - showAttachmentPanel() - } - } - binding.attachmentPanel.setOnClickListener c@{ - val message = replyMessage.value ?: return@c + val message = attachmentController.message.value ?: return@c val index = adapter.values.indexOf(message) if (index == -1) return@c @@ -225,9 +219,8 @@ class MessagesHistoryFragment : } binding.dismissReply.setOnClickListener { - if (replyMessage.value != null) replyMessage.value = null - - hideAttachmentPanel() + if (attachmentController.message.value != null) + attachmentController.message.value = null } } @@ -282,28 +275,6 @@ class MessagesHistoryFragment : binding.pin.isVisible = conversation.isPinned } - private fun showAttachmentPanel(duration: Long = 250) { - if (isAttachmentPanelVisible.value == false) isAttachmentPanelVisible.value = true - - binding.attachmentPanel.animate() - .translationY(0f) - .alpha(1f) - .setDuration(duration) - .withStartAction { binding.attachmentPanel.isVisible = true } - .start() - } - - private fun hideAttachmentPanel(duration: Long = 250) { - if (isAttachmentPanelVisible.value == true) isAttachmentPanelVisible.value = false - - binding.attachmentPanel.animate() - .alpha(0f) - .translationY(50f) - .setDuration(duration) - .withEndAction { binding.attachmentPanel.isVisible = false } - .start() - } - private fun performAction() { if (action.value == Action.RECORD) { return @@ -321,7 +292,7 @@ class MessagesHistoryFragment : fromId = UserConfig.userId, date = (date / 1000).toInt(), randomId = 0, - replyMessage = replyMessage.value + replyMessage = attachmentController.message.value ) adapter.add(message) @@ -329,10 +300,8 @@ class MessagesHistoryFragment : binding.recyclerView.smoothScrollToPosition(adapter.lastPosition) binding.message.clear() - val replyMessage = replyMessage.value - - this.replyMessage.value = null - hideAttachmentPanel() + val replyMessage = attachmentController.message.value + attachmentController.message.value = null viewModel.sendMessage( peerId = conversation.id, @@ -343,12 +312,13 @@ class MessagesHistoryFragment : } } - override fun onEvent(event: VKEvent) { + override fun onEvent(event: VkEvent) { super.onEvent(event) when (event) { is MessagesMarkAsImportant -> markMessagesAsImportant(event) is MessagesLoaded -> refreshMessages(event) + is MessagesPin -> conversation.pinnedMessage = event.message is StartProgressEvent -> onProgressStarted() is StopProgressEvent -> onProgressStopped() } @@ -436,106 +406,67 @@ class MessagesHistoryFragment : val message = adapter.values[position] if (message.action != null) return -// val popupMenu = PopupMenu(requireContext(), view) -// -// val reply = popupMenu.menu.add( -// getString(R.string.message_context_action_reply) -// ) -// -// reply.icon = -// ContextCompat.getDrawable( -// requireContext(), -// R.drawable.ic_attachment_wall_reply -// )?.constantState?.newDrawable()?.also { -// it.setTint( -// ContextCompat.getColor( -// requireContext(), -// R.color.textColorSecondaryVariant -// ) -// ) -// } -// -// val important = popupMenu.menu.add( -// getString( -// if (message.important) R.string.message_context_action_unmark_as_important -// else R.string.message_context_action_mark_as_important -// ) -// ) -// -// important.icon = -// ContextCompat.getDrawable( -// requireContext(), -// R.drawable.ic_star_border -// )?.constantState?.newDrawable()?.also { -// it.setTint( -// ContextCompat.getColor( -// requireContext(), -// R.color.textColorSecondaryVariant -// ) -// ) -// } -// -// popupMenu.setForceShowIcon(true) -// popupMenu.setOnMenuItemClickListener { -// when (it) { -// reply -> { -// val title = when { -// message.isGroup() && message.group.value != null -> message.group.value?.name -// message.isUser() && message.user.value != null -> message.user.value?.fullName -// else -> null -// } -// -// if (replyMessage.value != message) replyMessage.value = message -// -// binding.replyMessageTitle.text = title -// binding.replyMessageText.text = message.text ?: "[no_message]" -// -// if (binding.attachmentPanel.isNotVisible) binding.avatar.performClick() -// true -// } -// -// important -> { -// viewModel.markAsImportant( -// messagesIds = listOf(message.id), -// important = !message.important -// ) -// true -// } -// -// else -> false -// } -// } -// popupMenu.show() + val time = getString( + R.string.time_format, + SimpleDateFormat( + "dd.MM.yyyy, HH:mm:ss", + Locale.getDefault() + ).format(message.date * 1000L) + ) val reply = getString(R.string.message_context_action_reply) + val isMessageAlreadyPinned = message.id == conversation.pinnedMessage?.id + + val pin = getString( + if (isMessageAlreadyPinned) R.string.message_context_action_unpin + else R.string.message_context_action_pin + ) + + val edit = getString(R.string.message_context_action_edit) + val important = getString( if (message.important) R.string.message_context_action_unmark_as_important else R.string.message_context_action_mark_as_important ) - val params = arrayOf(reply, important) + val params = mutableListOf() + params.add(reply) + + if (conversation.canChangePin) { + params.add(pin) + } + + if (message.canEdit()) { + params.add(edit) + } + + params.add(important) + + val arrayParams = params.toTypedArray() val dialog = MaterialAlertDialogBuilder(requireContext()) - .setItems(params) { _, which -> + .setTitle(time) + .setItems(arrayParams) { _, which -> when (params[which]) { important -> viewModel.markAsImportant( messagesIds = listOf(message.id), important = !message.important ) reply -> { - val title = when { - message.isGroup() && message.group.value != null -> message.group.value?.name - message.isUser() && message.user.value != null -> message.user.value?.fullName - else -> null - } + if (attachmentController.message.value != message) + attachmentController.message.value = message + } + pin -> viewModel.pinMessage( + peerId = conversation.id, + messageId = message.id, + pin = !isMessageAlreadyPinned + ) + edit -> { + attachmentController.isEditing = true - if (replyMessage.value != message) replyMessage.value = message - - binding.replyMessageTitle.text = title - binding.replyMessageText.text = message.text ?: "[no_message]" - - if (binding.attachmentPanel.isNotVisible) binding.avatar.performClick() + if (attachmentController.message.value != message) + attachmentController.message.value = message } } } @@ -549,4 +480,78 @@ class MessagesHistoryFragment : return true } + private inner class AttachmentPanelController { + val isPanelVisible = MutableLiveData(false) + val message = MutableLiveData() + + var isEditing = false + + fun init(): AttachmentPanelController { + message.observe(viewLifecycleOwner) { value -> + if (value != null) { + applyMessage(value) + } else { + clearMessage() + } + } + + message.value = null + return this + } + + private fun applyMessage(message: VkMessage) { + showPanel() + + val title = when { + message.isGroup() && message.group.value != null -> message.group.value?.name + message.isUser() && message.user.value != null -> message.user.value?.fullName + else -> null + } + + binding.replyMessageTitle.text = title + binding.replyMessageText.text = message.text ?: "[no_message]" + + if (isEditing) { + binding.message.setText(message.text ?: "[no_message]") + } + } + + private fun clearMessage() { + hidePanel() + + binding.replyMessageTitle.clear() + binding.replyMessageText.clear() + + if (isEditing) { + isEditing = false + binding.message.clear() + } + } + + private fun showPanel(duration: Long = 250) { + if (attachmentController.isPanelVisible.value == false) + attachmentController.isPanelVisible.value = true + + binding.attachmentPanel.animate() + .translationY(0f) + .alpha(1f) + .setDuration(duration) + .withStartAction { binding.attachmentPanel.isVisible = true } + .start() + } + + private fun hidePanel(duration: Long = 250) { + if (attachmentController.isPanelVisible.value == true) + attachmentController.isPanelVisible.value = false + + binding.attachmentPanel.animate() + .alpha(0f) + .translationY(50f) + .setDuration(duration) + .withEndAction { binding.attachmentPanel.isVisible = false } + .start() + } + + } + } \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt index e552c313..f425a01b 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesHistoryViewModel.kt @@ -6,28 +6,24 @@ import com.meloda.fast.api.model.VkConversation import com.meloda.fast.api.model.VkGroup import com.meloda.fast.api.model.VkMessage import com.meloda.fast.api.model.VkUser -import com.meloda.fast.api.model.request.MessagesGetHistoryRequest -import com.meloda.fast.api.model.request.MessagesMarkAsImportantRequest -import com.meloda.fast.api.model.request.MessagesSendRequest +import com.meloda.fast.api.model.request.* import com.meloda.fast.api.network.datasource.MessagesDataSource import com.meloda.fast.base.viewmodel.BaseViewModel -import com.meloda.fast.base.viewmodel.StartProgressEvent -import com.meloda.fast.base.viewmodel.StopProgressEvent -import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.base.viewmodel.VkEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class MessagesHistoryViewModel @Inject constructor( - private val dataSource: MessagesDataSource + private val messages: MessagesDataSource ) : BaseViewModel() { fun loadHistory( peerId: Int ) = viewModelScope.launch { makeJob({ - dataSource.getHistory( + messages.getHistory( MessagesGetHistoryRequest( count = 30, peerId = peerId, @@ -53,18 +49,18 @@ class MessagesHistoryViewModel @Inject constructor( } } - val messages = hashMapOf() + val hashMessages = hashMapOf() response.items.forEach { baseMessage -> - baseMessage.asVkMessage().let { message -> messages[message.id] = message } + baseMessage.asVkMessage().let { message -> hashMessages[message.id] = message } } - dataSource.storeMessages(messages.values.toList()) + messages.store(hashMessages.values.toList()) val conversations = hashMapOf() response.conversations?.let { baseConversations -> baseConversations.forEach { baseConversation -> baseConversation.asVkConversation( - messages[baseConversation.last_message_id] + hashMessages[baseConversation.last_message_id] ).let { conversation -> conversations[conversation.id] = conversation } } } @@ -75,16 +71,10 @@ class MessagesHistoryViewModel @Inject constructor( profiles = profiles, groups = groups, conversations = conversations, - messages = messages.values.toList() + messages = hashMessages.values.toList() ) ) - }, - onError = { - val throwable = it - throw it - }, - onStart = { sendEvent(StartProgressEvent) }, - onEnd = { sendEvent(StopProgressEvent) }) + }) } fun sendMessage( @@ -96,7 +86,7 @@ class MessagesHistoryViewModel @Inject constructor( ) = viewModelScope.launch { makeJob( { - dataSource.send( + messages.send( MessagesSendRequest( peerId = peerId, randomId = randomId, @@ -108,10 +98,6 @@ class MessagesHistoryViewModel @Inject constructor( onAnswer = { val response = it.response ?: return@makeJob setId?.invoke(response) - }, - onError = { - val throwable = it - val i = 0 }) } @@ -120,7 +106,7 @@ class MessagesHistoryViewModel @Inject constructor( important: Boolean ) = viewModelScope.launch { makeJob({ - dataSource.markAsImportant( + messages.markAsImportant( MessagesMarkAsImportantRequest( messagesIds = messagesIds, important = important @@ -135,13 +121,39 @@ class MessagesHistoryViewModel @Inject constructor( important = important ) ) - }, - onError = { - val throwable = it - val i = 0 }) } + fun pinMessage( + peerId: Int, + messageId: Int? = null, + conversationMessageId: Int? = null, + pin: Boolean + ) = viewModelScope.launch { + if (pin) { + makeJob({ + messages.pin( + MessagesPinMessageRequest( + peerId = peerId, + messageId = messageId, + conversationMessageId = conversationMessageId + ) + ) + }, + onAnswer = { + val response = it.response ?: return@makeJob + sendEvent(MessagesPin(response.asVkMessage())) + } + ) + } else { + makeJob({ messages.unpin(MessagesUnPinMessageRequest(peerId = peerId)) }, + onAnswer = { + println("Fast::MessagesHistoryViewModel::unPin::Response::${it.response}") + sendEvent(MessagesUnpin) + } + ) + } + } } data class MessagesLoaded( @@ -150,9 +162,16 @@ data class MessagesLoaded( val messages: List, val profiles: HashMap, val groups: HashMap -) : VKEvent() +) : VkEvent() data class MessagesMarkAsImportant( val messagesIds: List, val important: Boolean -) : VKEvent() \ No newline at end of file +) : VkEvent() + +data class MessagesPin( + val message: VkMessage +) : VkEvent() + +object MessagesUnpin : VkEvent() + diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt index e0fad5f3..3140463a 100644 --- a/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/MessagesPreparator.kt @@ -9,7 +9,6 @@ import android.widget.Space import android.widget.TextView import androidx.appcompat.widget.LinearLayoutCompat import androidx.core.content.ContextCompat -import androidx.core.view.isInvisible import androidx.core.view.isVisible import coil.load import com.meloda.fast.R @@ -65,14 +64,17 @@ class MessagesPreparator constructor( ContextCompat.getDrawable(context, R.drawable.ic_message_out_background) private val backgroundMiddleOut = ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_middle) -// private val backgroundStrokeOut = -// ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_stroke) -// private val backgroundMiddleStrokeOut = -// ContextCompat.getDrawable(context, R.drawable.ic_message_out_background_middle_stroke) private val rootHighlightedColor = ContextCompat.getColor(context, R.color.n2_100) + private var photoClickListener: ((url: String) -> Unit)? = null + + fun setPhotoClickListener(unit: ((url: String) -> Unit)?): MessagesPreparator { + this.photoClickListener = unit + return this + } + fun prepare() { val messageUser: VkUser? = (if (message.isUser()) { profiles[message.fromId] @@ -104,20 +106,24 @@ class MessagesPreparator constructor( ) if (message.isPeerChat()) { - - val fromDiffSender = VkUtils.isPreviousMessageFromDifferentSender(prevMessage, message) + val prevSenderDiff = VkUtils.isPreviousMessageFromDifferentSender(prevMessage, message) + val nextSenderDiff = VkUtils.isPreviousMessageFromDifferentSender(message, nextMessage) val fiveMinAgo = VkUtils.isPreviousMessageSentFiveMinutesAgo(prevMessage, message) val change = (prevMessage?.date ?: 0) - message.date Log.d( "Fast::MessagesPreparator", - "text: ${message.text}; prevText: ${prevMessage?.text}; time change: $change; fromDiffSender: $fromDiffSender; fiveMinAgo: $fiveMinAgo; " + "text: ${message.text}; prevText: ${prevMessage?.text}; time change: $change; fromDiffSender: $prevSenderDiff; fiveMinAgo: $fiveMinAgo; " ) - title?.isVisible = fromDiffSender || fiveMinAgo + title?.isVisible = prevSenderDiff || fiveMinAgo - avatar?.isInvisible = fromDiffSender && fiveMinAgo + avatar?.visibility = + if (nextSenderDiff + || (fiveMinAgo && prevSenderDiff) + || (!prevSenderDiff && nextMessage == null) + ) View.VISIBLE else View.INVISIBLE } else { title?.isVisible = false avatar?.isVisible = false @@ -131,7 +137,6 @@ class MessagesPreparator constructor( } title.text = titleString - title.measure(0, 0) } } @@ -164,13 +169,16 @@ class MessagesPreparator constructor( attachmentContainer.removeAllViews() } else { attachmentContainer.isVisible = true + AttachmentInflater( context = context, container = attachmentContainer, message = message, groups = groups, profiles = profiles - ).inflate() + ) + .setPhotoClickListener(photoClickListener) + .inflate() } } } diff --git a/app/src/main/kotlin/com/meloda/fast/screens/photos/PhotoViewFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/photos/PhotoViewFragment.kt new file mode 100644 index 00000000..f3f99443 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/photos/PhotoViewFragment.kt @@ -0,0 +1,48 @@ +package com.meloda.fast.screens.photos + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.fragment.app.viewModels +import com.meloda.fast.base.BaseViewModelFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class PhotoViewFragment : BaseViewModelFragment() { + + override val viewModel: PhotoViewViewModel by viewModels() + +// private val photosList: MutableList = mutableListOf() + + private var photoLink: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + photoLink = requireArguments().getString("photoLink") + +// val list: List<*>? = Gson().fromJson( +// requireArguments().getString("photosList"), +// List::class.java +// ) +// +// list?.forEach { if (it is VkPhoto) photosList.add(it) } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ImageView(requireContext()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + photoLink?.let { viewModel.loadImageFromUrl(it, requireView() as ImageView) } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/photos/PhotoViewViewModel.kt b/app/src/main/kotlin/com/meloda/fast/screens/photos/PhotoViewViewModel.kt new file mode 100644 index 00000000..b9a2984d --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/photos/PhotoViewViewModel.kt @@ -0,0 +1,22 @@ +package com.meloda.fast.screens.photos + +import android.widget.ImageView +import androidx.lifecycle.viewModelScope +import coil.load +import com.meloda.fast.base.viewmodel.BaseViewModel +import kotlinx.coroutines.launch + +class PhotoViewViewModel : BaseViewModel() { + + fun loadImageFromUrl( + url: String, + imageView: ImageView + ) = viewModelScope.launch { + imageView.load(url) + } + + fun saveImageToLocalStorage(url: String) = viewModelScope.launch { + TODO("Not implemented") + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt index f300bbe2..f934a9e4 100644 --- a/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt +++ b/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt @@ -7,6 +7,8 @@ import java.util.* object TimeUtils { + const val ONE_DAY_IN_SECONDS = 86400 + fun removeTime(date: Date): Long { return Calendar.getInstance().apply { time = date diff --git a/app/src/main/res/drawable/ic_round_done_24.xml b/app/src/main/res/drawable/ic_round_done_24.xml new file mode 100644 index 00000000..2231d757 --- /dev/null +++ b/app/src/main/res/drawable/ic_round_done_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_messages_history.xml b/app/src/main/res/layout/fragment_messages_history.xml index 0dddcff8..08338bec 100644 --- a/app/src/main/res/layout/fragment_messages_history.xml +++ b/app/src/main/res/layout/fragment_messages_history.xml @@ -219,8 +219,10 @@ android:minHeight="105dp" android:orientation="vertical" android:padding="16dp" + android:visibility="gone" app:layout_anchor="@+id/messagePanel" - app:layout_anchorGravity="center_vertical|top"> + app:layout_anchorGravity="center_vertical|top" + tools:visibility="visible"> + android:tint="@color/n1_800" /> diff --git a/app/src/main/res/layout/item_message_in.xml b/app/src/main/res/layout/item_message_in.xml index f54f2d22..a142f1e8 100644 --- a/app/src/main/res/layout/item_message_in.xml +++ b/app/src/main/res/layout/item_message_in.xml @@ -70,19 +70,6 @@ android:textColor="@color/n1_800" tools:text="This" /> - - - - @@ -96,6 +83,20 @@ android:src="@color/a3_200" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_message_out.xml b/app/src/main/res/layout/item_message_out.xml index 0b46e0e2..6b836505 100644 --- a/app/src/main/res/layout/item_message_out.xml +++ b/app/src/main/res/layout/item_message_out.xml @@ -1,6 +1,5 @@ - + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|start" + android:padding="15dp" + android:textColor="@color/n1_900" + tools:text="This is test" /> - - - - - - - + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/messages.xml b/app/src/main/res/navigation/messages.xml index 8db6e5ac..911ca742 100644 --- a/app/src/main/res/navigation/messages.xml +++ b/app/src/main/res/navigation/messages.xml @@ -21,6 +21,17 @@ android:id="@+id/messagesHistoryFragment" android:name="com.meloda.fast.screens.messages.MessagesHistoryFragment" android:label="MessagesHistoryFragment" - tools:layout="@layout/fragment_messages_history" /> + tools:layout="@layout/fragment_messages_history"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 04e57b21..d51307e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,4 +117,8 @@ Reply Mark as important Unmark as important + Time: %s + Pin + Unpin + Edit