move all ui-related classes and files to ui module
This commit is contained in:
@@ -9,7 +9,7 @@ import com.meloda.app.fast.model.BottomNavigationItem
|
|||||||
import com.meloda.app.fast.presentation.MainScreen
|
import com.meloda.app.fast.presentation.MainScreen
|
||||||
import com.meloda.app.fast.profile.navigation.Profile
|
import com.meloda.app.fast.profile.navigation.Profile
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import com.meloda.app.fast.designsystem.R as UiR
|
import com.meloda.app.fast.ui.R as UiR
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
object MainGraph
|
object MainGraph
|
||||||
|
|||||||
@@ -37,15 +37,15 @@ import com.meloda.app.fast.common.extensions.isSdkAtLeast
|
|||||||
import com.meloda.app.fast.datastore.SettingsController
|
import com.meloda.app.fast.datastore.SettingsController
|
||||||
import com.meloda.app.fast.datastore.UserSettings
|
import com.meloda.app.fast.datastore.UserSettings
|
||||||
import com.meloda.app.fast.datastore.model.LongPollState
|
import com.meloda.app.fast.datastore.model.LongPollState
|
||||||
import com.meloda.app.fast.datastore.model.ThemeConfig
|
|
||||||
import com.meloda.app.fast.designsystem.AppTheme
|
|
||||||
import com.meloda.app.fast.designsystem.LocalTheme
|
|
||||||
import com.meloda.app.fast.service.OnlineService
|
import com.meloda.app.fast.service.OnlineService
|
||||||
import com.meloda.app.fast.service.longpolling.LongPollingService
|
import com.meloda.app.fast.service.longpolling.LongPollingService
|
||||||
|
import com.meloda.app.fast.ui.model.ThemeConfig
|
||||||
|
import com.meloda.app.fast.ui.theme.AppTheme
|
||||||
|
import com.meloda.app.fast.ui.theme.LocalTheme
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.compose.KoinContext
|
import org.koin.compose.KoinContext
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
import com.meloda.app.fast.designsystem.R as UiR
|
import com.meloda.app.fast.ui.R as UiR
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import androidx.navigation.compose.NavHost
|
|||||||
import androidx.navigation.compose.navigation
|
import androidx.navigation.compose.navigation
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.meloda.app.fast.conversations.navigation.conversationsScreen
|
import com.meloda.app.fast.conversations.navigation.conversationsScreen
|
||||||
import com.meloda.app.fast.designsystem.LocalBottomPadding
|
import com.meloda.app.fast.ui.theme.LocalBottomPadding
|
||||||
import com.meloda.app.fast.designsystem.LocalHazeState
|
import com.meloda.app.fast.ui.theme.LocalHazeState
|
||||||
import com.meloda.app.fast.designsystem.LocalTheme
|
import com.meloda.app.fast.ui.theme.LocalTheme
|
||||||
import com.meloda.app.fast.friends.navigation.friendsScreen
|
import com.meloda.app.fast.friends.navigation.friendsScreen
|
||||||
import com.meloda.app.fast.model.BaseError
|
import com.meloda.app.fast.model.BaseError
|
||||||
import com.meloda.app.fast.model.BottomNavigationItem
|
import com.meloda.app.fast.model.BottomNavigationItem
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import com.meloda.app.fast.auth.authNavGraph
|
|||||||
import com.meloda.app.fast.auth.navigateToAuth
|
import com.meloda.app.fast.auth.navigateToAuth
|
||||||
import com.meloda.app.fast.chatmaterials.navigation.chatMaterialsScreen
|
import com.meloda.app.fast.chatmaterials.navigation.chatMaterialsScreen
|
||||||
import com.meloda.app.fast.chatmaterials.navigation.navigateToChatMaterials
|
import com.meloda.app.fast.chatmaterials.navigation.navigateToChatMaterials
|
||||||
import com.meloda.app.fast.designsystem.R
|
import com.meloda.app.fast.ui.R
|
||||||
import com.meloda.app.fast.languagepicker.navigation.languagePickerScreen
|
import com.meloda.app.fast.languagepicker.navigation.languagePickerScreen
|
||||||
import com.meloda.app.fast.languagepicker.navigation.navigateToLanguagePicker
|
import com.meloda.app.fast.languagepicker.navigation.navigateToLanguagePicker
|
||||||
import com.meloda.app.fast.messageshistory.navigation.messagesHistoryScreen
|
import com.meloda.app.fast.messageshistory.navigation.messagesHistoryScreen
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.meloda.app.fast.datastore.SettingsController
|
|||||||
import com.meloda.app.fast.datastore.SettingsKeys
|
import com.meloda.app.fast.datastore.SettingsKeys
|
||||||
import com.meloda.app.fast.datastore.UserSettings
|
import com.meloda.app.fast.datastore.UserSettings
|
||||||
import com.meloda.app.fast.datastore.model.LongPollState
|
import com.meloda.app.fast.datastore.model.LongPollState
|
||||||
import com.meloda.app.fast.designsystem.R
|
import com.meloda.app.fast.ui.R
|
||||||
import com.meloda.app.fast.model.api.data.LongPollUpdates
|
import com.meloda.app.fast.model.api.data.LongPollUpdates
|
||||||
import com.meloda.app.fast.model.api.data.VkLongPollData
|
import com.meloda.app.fast.model.api.data.VkLongPollData
|
||||||
import com.meloda.app.fast.util.NotificationsUtils
|
import com.meloda.app.fast.util.NotificationsUtils
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import android.content.Context
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import com.meloda.app.fast.common.AppConstants
|
import com.meloda.app.fast.common.AppConstants
|
||||||
import com.meloda.app.fast.designsystem.R as UiR
|
import com.meloda.app.fast.ui.R as UiR
|
||||||
|
|
||||||
object NotificationsUtils {
|
object NotificationsUtils {
|
||||||
|
|
||||||
|
|||||||
@@ -1,721 +0,0 @@
|
|||||||
package com.meloda.app.fast.common.util
|
|
||||||
|
|
||||||
//import android.content.Context
|
|
||||||
//import androidx.compose.ui.graphics.Color
|
|
||||||
//import androidx.compose.ui.text.AnnotatedString
|
|
||||||
//import androidx.compose.ui.text.SpanStyle
|
|
||||||
//import androidx.compose.ui.text.buildAnnotatedString
|
|
||||||
//import androidx.compose.ui.text.font.FontWeight
|
|
||||||
//import androidx.compose.ui.text.withStyle
|
|
||||||
//import com.meloda.app.fast.common.UiImage
|
|
||||||
//import com.meloda.app.fast.common.UiText
|
|
||||||
//import com.meloda.app.fast.common.extensions.orDots
|
|
||||||
//import com.meloda.app.fast.common.parseString
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
//object VkUtils {
|
|
||||||
//
|
|
||||||
// fun prepareMessageText(text: String, forConversations: Boolean = false): String {
|
|
||||||
// return text.apply {
|
|
||||||
// if (forConversations) {
|
|
||||||
// replace("\n", " ")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// replace("&", "&")
|
|
||||||
// replace(""", "\"")
|
|
||||||
// replace("<br>", "\n")
|
|
||||||
// replace(">", ">")
|
|
||||||
// replace("<", "<")
|
|
||||||
// replace("<br/>", "\n")
|
|
||||||
// replace("–", "-")
|
|
||||||
// trim()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun parseAttachments(baseAttachments: List<VkAttachmentItemData>?): List<VkAttachment>? {
|
|
||||||
// if (baseAttachments.isNullOrEmpty()) return null
|
|
||||||
//
|
|
||||||
// val attachments = mutableListOf<VkAttachment>()
|
|
||||||
//
|
|
||||||
// for (baseAttachment in baseAttachments) {
|
|
||||||
// when (baseAttachment.getPreparedType()) {
|
|
||||||
// AttachmentType.UNKNOWN -> continue
|
|
||||||
//
|
|
||||||
// AttachmentType.PHOTO -> {
|
|
||||||
// val photo = baseAttachment.photo ?: continue
|
|
||||||
// attachments += photo.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.VIDEO -> {
|
|
||||||
// val video = baseAttachment.video ?: continue
|
|
||||||
// attachments += video.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.AUDIO -> {
|
|
||||||
// val audio = baseAttachment.audio ?: continue
|
|
||||||
// attachments += audio.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.FILE -> {
|
|
||||||
// val file = baseAttachment.file ?: continue
|
|
||||||
// attachments += file.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.LINK -> {
|
|
||||||
// val link = baseAttachment.link ?: continue
|
|
||||||
// attachments += link.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.MINI_APP -> {
|
|
||||||
// val miniApp = baseAttachment.miniApp ?: continue
|
|
||||||
// attachments += miniApp.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.AUDIO_MESSAGE -> {
|
|
||||||
// val voiceMessage = baseAttachment.voiceMessage ?: continue
|
|
||||||
// attachments += voiceMessage.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.STICKER -> {
|
|
||||||
// val sticker = baseAttachment.sticker ?: continue
|
|
||||||
// attachments += sticker.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.GIFT -> {
|
|
||||||
// val gift = baseAttachment.gift ?: continue
|
|
||||||
// attachments += gift.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.WALL -> {
|
|
||||||
// val wall = baseAttachment.wall ?: continue
|
|
||||||
// attachments += wall.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.GRAFFITI -> {
|
|
||||||
// val graffiti = baseAttachment.graffiti ?: continue
|
|
||||||
// attachments += graffiti.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.POLL -> {
|
|
||||||
// val poll = baseAttachment.poll ?: continue
|
|
||||||
// attachments += poll.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.WALL_REPLY -> {
|
|
||||||
// val wallReply = baseAttachment.wallReply ?: continue
|
|
||||||
// attachments += wallReply.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.CALL -> {
|
|
||||||
// val call = baseAttachment.call ?: continue
|
|
||||||
// attachments += call.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.GROUP_CALL_IN_PROGRESS -> {
|
|
||||||
// val groupCall = baseAttachment.groupCall ?: continue
|
|
||||||
// attachments += groupCall.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.CURATOR -> {
|
|
||||||
// val curator = baseAttachment.curator ?: continue
|
|
||||||
// attachments += curator.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.EVENT -> {
|
|
||||||
// val event = baseAttachment.event ?: continue
|
|
||||||
// attachments += event.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.STORY -> {
|
|
||||||
// val story = baseAttachment.story ?: continue
|
|
||||||
// attachments += story.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.WIDGET -> {
|
|
||||||
// val widget = baseAttachment.widget ?: continue
|
|
||||||
// attachments += widget.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.ARTIST -> {
|
|
||||||
// val artist = baseAttachment.artist ?: continue
|
|
||||||
// attachments += artist.toDomain()
|
|
||||||
//
|
|
||||||
// val audios = baseAttachment.audios ?: continue
|
|
||||||
// audios.map(VkAudioData::toDomain).let(attachments::addAll)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.AUDIO_PLAYLIST -> {
|
|
||||||
// val audioPlaylist = baseAttachment.audioPlaylist ?: continue
|
|
||||||
// attachments += audioPlaylist.toDomain()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.PODCAST -> {
|
|
||||||
// val podcast = baseAttachment.podcast ?: continue
|
|
||||||
// attachments += podcast.toDomain()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return attachments
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun getActionMessageText(
|
|
||||||
// context: Context,
|
|
||||||
// message: VkMessage?,
|
|
||||||
// youPrefix: String,
|
|
||||||
// messageUser: VkUserDomain?,
|
|
||||||
// messageGroup: VkGroupDomain?,
|
|
||||||
// action: VkMessage.Action?,
|
|
||||||
// actionUser: VkUserDomain?,
|
|
||||||
// actionGroup: VkGroupDomain?,
|
|
||||||
// ): AnnotatedString? {
|
|
||||||
// return when {
|
|
||||||
// message == null -> null
|
|
||||||
// action == null -> null
|
|
||||||
//
|
|
||||||
// else -> buildAnnotatedString {
|
|
||||||
// when (action) {
|
|
||||||
// VkMessage.Action.CHAT_CREATE -> {
|
|
||||||
// val text = message.actionText ?: return null
|
|
||||||
//
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// val string = UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_created,
|
|
||||||
// listOf(prefix, text)
|
|
||||||
// ).parseString(context).orEmpty()
|
|
||||||
//
|
|
||||||
// append(string)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// val textStartIndex = string.indexOf(text)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = textStartIndex,
|
|
||||||
// end = textStartIndex + text.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_TITLE_UPDATE -> {
|
|
||||||
// val text = message.actionText ?: return null
|
|
||||||
//
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// val string = UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_renamed,
|
|
||||||
// listOf(prefix, text)
|
|
||||||
// ).parseString(context).orEmpty()
|
|
||||||
//
|
|
||||||
// append(string)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// val textStartIndex = string.indexOf(text)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = textStartIndex,
|
|
||||||
// end = textStartIndex + text.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_PHOTO_UPDATE -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_photo_update,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_PHOTO_REMOVE -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_photo_remove,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_KICK_USER -> {
|
|
||||||
// val memberId = message.actionMemberId ?: return null
|
|
||||||
// val isUser = memberId > 0
|
|
||||||
// val isGroup = memberId < 0
|
|
||||||
//
|
|
||||||
// if (isUser && actionUser == null) return null
|
|
||||||
// if (isGroup && actionGroup == null) return null
|
|
||||||
//
|
|
||||||
// if (memberId == message.fromId) {
|
|
||||||
// val prefix =
|
|
||||||
// if (memberId == UserConfig.userId) youPrefix
|
|
||||||
// else actionUser.toString()
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_left,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// val prefix =
|
|
||||||
// if (message.fromId == UserConfig.userId) youPrefix
|
|
||||||
// else messageUser?.toString() ?: messageGroup?.toString().orDots()
|
|
||||||
//
|
|
||||||
// val postfix =
|
|
||||||
// if (memberId == UserConfig.userId) youPrefix.lowercase()
|
|
||||||
// else actionUser.toString()
|
|
||||||
//
|
|
||||||
// val string = UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_kicked,
|
|
||||||
// listOf(prefix, postfix)
|
|
||||||
// ).parseString(context).orEmpty()
|
|
||||||
//
|
|
||||||
// append(string)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// val postfixStartIndex = string.indexOf(postfix)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = postfixStartIndex,
|
|
||||||
// end = postfixStartIndex + postfix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_INVITE_USER -> {
|
|
||||||
// val memberId = message.actionMemberId ?: 0
|
|
||||||
// val isUser = memberId > 0
|
|
||||||
// val isGroup = memberId < 0
|
|
||||||
//
|
|
||||||
// if (isUser && actionUser == null) return null
|
|
||||||
// if (isGroup && actionGroup == null) return null
|
|
||||||
//
|
|
||||||
// if (memberId == message.fromId) {
|
|
||||||
// val prefix =
|
|
||||||
// if (memberId == UserConfig.userId) youPrefix
|
|
||||||
// else actionUser.toString()
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_returned,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// val prefix =
|
|
||||||
// if (message.fromId == UserConfig.userId) youPrefix
|
|
||||||
// else messageUser?.toString() ?: messageGroup?.toString().orDots()
|
|
||||||
//
|
|
||||||
// val postfix =
|
|
||||||
// if (memberId == UserConfig.userId) youPrefix.lowercase()
|
|
||||||
// else actionUser.toString()
|
|
||||||
//
|
|
||||||
// val string = UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_invited,
|
|
||||||
// listOf(prefix, postfix)
|
|
||||||
// ).parseString(context).orEmpty()
|
|
||||||
//
|
|
||||||
// append(string)
|
|
||||||
//
|
|
||||||
// val postfixStartIndex = string.indexOf(postfix)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = postfixStartIndex,
|
|
||||||
// end = postfixStartIndex + postfix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_INVITE_USER_BY_LINK -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_joined_by_link,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_INVITE_USER_BY_CALL -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_joined_by_call,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_INVITE_USER_BY_CALL_LINK -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_user_joined_by_call_link,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_PIN_MESSAGE -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_pin_message,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_unpin_message,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_SCREENSHOT -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isGroup() -> messageGroup?.name
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_screenshot,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// VkMessage.Action.CHAT_STYLE_UPDATE -> {
|
|
||||||
// val prefix = when {
|
|
||||||
// message.fromId == UserConfig.userId -> youPrefix
|
|
||||||
// message.isUser() -> messageUser?.toString()
|
|
||||||
// else -> return null
|
|
||||||
// } ?: return null
|
|
||||||
//
|
|
||||||
// UiText.ResourceParams(
|
|
||||||
// UiR.string.message_action_chat_style_update,
|
|
||||||
// listOf(prefix)
|
|
||||||
// ).parseString(context).orEmpty().let(::append)
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(fontWeight = FontWeight.SemiBold),
|
|
||||||
// start = 0,
|
|
||||||
// end = prefix.length
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun getForwardsText(context: Context, message: VkMessage?): AnnotatedString? {
|
|
||||||
// return when {
|
|
||||||
// message == null -> null
|
|
||||||
//
|
|
||||||
// message.hasForwards() -> buildAnnotatedString {
|
|
||||||
// val forwards = message.forwards.orEmpty()
|
|
||||||
//
|
|
||||||
// withStyle(style = SpanStyle(fontWeight = FontWeight.SemiBold)) {
|
|
||||||
// append(
|
|
||||||
// UiText.Resource(
|
|
||||||
// if (forwards.size == 1) UiR.string.forwarded_message
|
|
||||||
// else UiR.string.forwarded_messages
|
|
||||||
// ).parseString(context)
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// else -> null
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun getAttachmentText(
|
|
||||||
// getText: (UiText) -> String,
|
|
||||||
// message: VkMessage?
|
|
||||||
// ): AnnotatedString? {
|
|
||||||
// return when {
|
|
||||||
// message == null -> null
|
|
||||||
//
|
|
||||||
// message.geoType != null -> buildAnnotatedString {
|
|
||||||
// withStyle(style = SpanStyle(fontWeight = FontWeight.SemiBold)) {
|
|
||||||
// when (message.geoType) {
|
|
||||||
// "point" -> getText(UiText.Resource(UiR.string.message_geo_point))
|
|
||||||
// .let(::append)
|
|
||||||
//
|
|
||||||
// else -> getText(UiText.Resource(UiR.string.message_geo))
|
|
||||||
// .let(::append)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// message.hasAttachments() -> buildAnnotatedString {
|
|
||||||
// val attachments = message.attachments.orEmpty()
|
|
||||||
//
|
|
||||||
// withStyle(style = SpanStyle(fontWeight = FontWeight.SemiBold)) {
|
|
||||||
// if (attachments.size == 1) {
|
|
||||||
// getText(getAttachmentUiText(attachments.first())).let(::append)
|
|
||||||
// } else {
|
|
||||||
// when {
|
|
||||||
// isAttachmentsHaveOneType(attachments) -> {
|
|
||||||
// getText(getAttachmentUiText(attachments.first(), attachments.size))
|
|
||||||
// .let(::append)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// attachments.any { it.type == AttachmentType.ARTIST } -> {
|
|
||||||
// getText(
|
|
||||||
// getAttachmentUiText(attachments.first { it.type == AttachmentType.ARTIST })
|
|
||||||
// ).let(::append)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// else -> {
|
|
||||||
// getText(UiText.Resource(UiR.string.message_attachments_many))
|
|
||||||
// .let(::append)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// else -> null
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun getAttachmentConversationIcon(message: VkMessage?): UiImage? {
|
|
||||||
// return message?.attachments?.let { attachments ->
|
|
||||||
// if (attachments.isEmpty()) return null
|
|
||||||
// if (attachments.size == 1 || isAttachmentsHaveOneType(attachments)) {
|
|
||||||
// message.geoType?.let {
|
|
||||||
// return UiImage.Resource(UiR.drawable.ic_map_marker)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// getAttachmentIconByType(attachments.first().type)
|
|
||||||
// } else {
|
|
||||||
// UiImage.Resource(UiR.drawable.ic_baseline_attach_file_24)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// fun getAttachmentUiText(
|
|
||||||
// attachment: VkAttachment,
|
|
||||||
// size: Int = 1,
|
|
||||||
// ): UiText {
|
|
||||||
// if (attachment.type.isMultiple()) {
|
|
||||||
// return when (attachment.type) {
|
|
||||||
// AttachmentType.PHOTO -> UiR.plurals.attachment_photos
|
|
||||||
// AttachmentType.VIDEO -> UiR.plurals.attachment_videos
|
|
||||||
// AttachmentType.AUDIO -> UiR.plurals.attachment_audios
|
|
||||||
// AttachmentType.FILE -> UiR.plurals.attachment_files
|
|
||||||
// else -> throw IllegalArgumentException("Unknown multiple type: ${attachment.type}")
|
|
||||||
// }.let { resId -> UiText.QuantityResource(resId, size) }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return when (attachment.type) {
|
|
||||||
// AttachmentType.UNKNOWN,
|
|
||||||
// AttachmentType.PHOTO,
|
|
||||||
// AttachmentType.VIDEO,
|
|
||||||
// AttachmentType.AUDIO,
|
|
||||||
// AttachmentType.FILE -> {
|
|
||||||
// throw IllegalArgumentException("Unknown multiple type: ${attachment.type}")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// AttachmentType.LINK -> UiR.string.message_attachments_link
|
|
||||||
// AttachmentType.AUDIO_MESSAGE -> UiR.string.message_attachments_audio_message
|
|
||||||
// AttachmentType.MINI_APP -> UiR.string.message_attachments_mini_app
|
|
||||||
// AttachmentType.STICKER -> UiR.string.message_attachments_sticker
|
|
||||||
// AttachmentType.GIFT -> UiR.string.message_attachments_gift
|
|
||||||
// AttachmentType.WALL -> UiR.string.message_attachments_wall
|
|
||||||
// AttachmentType.GRAFFITI -> UiR.string.message_attachments_graffiti
|
|
||||||
// AttachmentType.POLL -> UiR.string.message_attachments_poll
|
|
||||||
// AttachmentType.WALL_REPLY -> UiR.string.message_attachments_wall_reply
|
|
||||||
// AttachmentType.CALL -> UiR.string.message_attachments_call
|
|
||||||
// AttachmentType.GROUP_CALL_IN_PROGRESS -> UiR.string.message_attachments_call_in_progress
|
|
||||||
// AttachmentType.CURATOR -> UiR.string.message_attachments_curator
|
|
||||||
// AttachmentType.EVENT -> UiR.string.message_attachments_event
|
|
||||||
// AttachmentType.STORY -> UiR.string.message_attachments_story
|
|
||||||
// AttachmentType.WIDGET -> UiR.string.message_attachments_widget
|
|
||||||
// AttachmentType.ARTIST -> UiR.string.message_attachments_artist
|
|
||||||
// AttachmentType.AUDIO_PLAYLIST -> UiR.string.message_attachments_audio_playlist
|
|
||||||
// AttachmentType.PODCAST -> UiR.string.message_attachments_podcast
|
|
||||||
// }.let(UiText::Resource)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun getTextWithVisualizedMentions(
|
|
||||||
// originalText: String,
|
|
||||||
// mentionColor: Color,
|
|
||||||
// ): AnnotatedString = buildAnnotatedString {
|
|
||||||
// val regex = """\[(id|club)(\d+)\|([^]]+)]""".toRegex()
|
|
||||||
//
|
|
||||||
// val mentions = mutableListOf<MentionIndex>()
|
|
||||||
//
|
|
||||||
// var currentIndex = 0
|
|
||||||
// val replacements = mutableListOf<Pair<IntRange, String>>()
|
|
||||||
//
|
|
||||||
// // TODO: 25/04/2024, Danil Nikolaev: check why not working ([id279494346|@iworld2rist] да убери ты Елену Шлипс от меня)
|
|
||||||
// val result = regex.replace(originalText) { matchResult ->
|
|
||||||
// val idPrefix = matchResult.groups[1]?.value.orEmpty()
|
|
||||||
// val startIndex = matchResult.range.first
|
|
||||||
// val endIndex = matchResult.range.last
|
|
||||||
//
|
|
||||||
// val id = matchResult.groups[2]?.value ?: ""
|
|
||||||
// val text = matchResult.groups[3]?.value ?: ""
|
|
||||||
//
|
|
||||||
// val replaced =
|
|
||||||
// text.substring(startIndex, endIndex + 1)
|
|
||||||
// .replace("[$idPrefix$id|$text]", text)
|
|
||||||
//
|
|
||||||
// val indexRange =
|
|
||||||
// (startIndex + currentIndex)..startIndex + currentIndex + replaced.length
|
|
||||||
//
|
|
||||||
// replacements.add(indexRange to replaced)
|
|
||||||
//
|
|
||||||
// mentions += MentionIndex(
|
|
||||||
// id = id.toIntOrNull() ?: -1,
|
|
||||||
// idPrefix = idPrefix,
|
|
||||||
// indexRange = indexRange
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// currentIndex += replaced.length - (endIndex - startIndex + 1)
|
|
||||||
//
|
|
||||||
// replaced
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// append(result)
|
|
||||||
//
|
|
||||||
// mentions.forEach { mention ->
|
|
||||||
// val startIndex = mention.indexRange.first
|
|
||||||
// val endIndex = mention.indexRange.last
|
|
||||||
//
|
|
||||||
// addStyle(
|
|
||||||
// style = SpanStyle(color = mentionColor),
|
|
||||||
// start = startIndex,
|
|
||||||
// end = endIndex
|
|
||||||
// )
|
|
||||||
// addStringAnnotation(
|
|
||||||
// tag = mention.idPrefix,
|
|
||||||
// annotation = mention.id.toString(),
|
|
||||||
// start = startIndex,
|
|
||||||
// end = endIndex
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
@@ -2,10 +2,8 @@ package com.meloda.app.fast.data
|
|||||||
|
|
||||||
import com.meloda.app.fast.network.OAuthErrorDomain
|
import com.meloda.app.fast.network.OAuthErrorDomain
|
||||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||||
|
import com.meloda.app.fast.network.VkErrorCode
|
||||||
import com.slack.eithernet.ApiResult
|
import com.slack.eithernet.ApiResult
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.filterIsInstance
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
|
|
||||||
sealed class State<out T> {
|
sealed class State<out T> {
|
||||||
|
|
||||||
@@ -16,7 +14,7 @@ sealed class State<out T> {
|
|||||||
sealed class Error : State<Nothing>() {
|
sealed class Error : State<Nothing>() {
|
||||||
|
|
||||||
data class ApiError(
|
data class ApiError(
|
||||||
val errorCode: Int,
|
val errorCode: VkErrorCode,
|
||||||
val errorMessage: String,
|
val errorMessage: String,
|
||||||
) : Error()
|
) : Error()
|
||||||
|
|
||||||
@@ -61,19 +59,9 @@ inline fun <T> State<T>.processState(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T, R> Flow<State<T>>.mapSuccess(
|
|
||||||
crossinline transform: suspend (value: T) -> R
|
|
||||||
): Flow<R> = filterIsInstance<State.Success<T>>()
|
|
||||||
.map { state -> transform.invoke(state.data) }
|
|
||||||
|
|
||||||
fun RestApiErrorDomain?.toStateApiError(): State.Error = when (this) {
|
fun RestApiErrorDomain?.toStateApiError(): State.Error = when (this) {
|
||||||
null -> State.Error.ConnectionError
|
null -> State.Error.ConnectionError
|
||||||
else -> State.Error.ApiError(code, message)
|
else -> State.Error.ApiError(VkErrorCode.parse(code), message)
|
||||||
}
|
|
||||||
|
|
||||||
fun OAuthErrorDomain?.toStateApiError(): State.Error = when (this) {
|
|
||||||
null -> State.Error.ConnectionError
|
|
||||||
else -> State.Error.OAuthError(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> ApiResult<T, RestApiErrorDomain>.mapToState() = when (this) {
|
fun <T : Any> ApiResult<T, RestApiErrorDomain>.mapToState() = when (this) {
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
package com.meloda.app.fast.data.api.auth
|
package com.meloda.app.fast.data.api.auth
|
||||||
|
|
||||||
import com.meloda.app.fast.model.api.requests.AuthDirectRequest
|
import com.meloda.app.fast.model.api.responses.ValidatePhoneResponse
|
||||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||||
import com.meloda.app.fast.model.api.responses.SendSmsResponse
|
|
||||||
import com.meloda.app.fast.network.OAuthErrorDomain
|
|
||||||
import com.slack.eithernet.ApiResult
|
import com.slack.eithernet.ApiResult
|
||||||
|
|
||||||
interface AuthRepository {
|
interface AuthRepository {
|
||||||
|
|
||||||
// suspend fun auth(
|
suspend fun validatePhone(
|
||||||
// params: AuthDirectRequest
|
|
||||||
// ): ApiResult<AuthDirectResponse, OAuthErrorDomain>
|
|
||||||
|
|
||||||
suspend fun sendSms(
|
|
||||||
validationSid: String
|
validationSid: String
|
||||||
): SendSmsResponse
|
): ApiResult<ValidatePhoneResponse, RestApiErrorDomain>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,20 @@
|
|||||||
package com.meloda.app.fast.data.api.auth
|
package com.meloda.app.fast.data.api.auth
|
||||||
|
|
||||||
import com.meloda.app.fast.model.api.requests.AuthDirectRequest
|
import com.meloda.app.fast.model.api.responses.ValidatePhoneResponse
|
||||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||||
import com.meloda.app.fast.model.api.responses.SendSmsResponse
|
import com.meloda.app.fast.network.mapApiDefault
|
||||||
import com.meloda.app.fast.network.OAuthErrorDomain
|
|
||||||
import com.meloda.app.fast.network.service.auth.AuthService
|
import com.meloda.app.fast.network.service.auth.AuthService
|
||||||
import com.slack.eithernet.ApiResult
|
import com.slack.eithernet.ApiResult
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class AuthRepositoryImpl(
|
class AuthRepositoryImpl(
|
||||||
private val authService: AuthService
|
private val service: AuthService
|
||||||
) : AuthRepository {
|
) : AuthRepository {
|
||||||
|
|
||||||
// override suspend fun auth(
|
override suspend fun validatePhone(
|
||||||
// params: AuthDirectRequest
|
|
||||||
// ): ApiResult<AuthDirectResponse, OAuthErrorDomain> {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: 05/05/2024, Danil Nikolaev: implement
|
|
||||||
override suspend fun sendSms(
|
|
||||||
validationSid: String
|
validationSid: String
|
||||||
): SendSmsResponse = withContext(Dispatchers.IO) {
|
): ApiResult<ValidatePhoneResponse, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||||
SendSmsResponse(
|
service.validatePhone(validationSid).mapApiDefault()
|
||||||
validationSid = null, delay = null, validationType = null, validationResend = null
|
|
||||||
|
|
||||||
)
|
|
||||||
// authService.sendSms(validationSid).mapResult(
|
|
||||||
// successMapper = { response -> response.requireResponse() },
|
|
||||||
// errorMapper = { error -> error?.toDomain() }
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -6,7 +6,7 @@ import com.meloda.app.fast.model.api.requests.LongPollGetUpdatesRequest
|
|||||||
import com.meloda.app.fast.model.api.requests.MessagesGetLongPollServerRequest
|
import com.meloda.app.fast.model.api.requests.MessagesGetLongPollServerRequest
|
||||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||||
import com.meloda.app.fast.network.mapApiResult
|
import com.meloda.app.fast.network.mapApiResult
|
||||||
import com.meloda.app.fast.network.mapResult
|
import com.meloda.app.fast.network.mapDefault
|
||||||
import com.meloda.app.fast.network.service.longpoll.LongPollService
|
import com.meloda.app.fast.network.service.longpoll.LongPollService
|
||||||
import com.meloda.app.fast.network.service.messages.MessagesService
|
import com.meloda.app.fast.network.service.messages.MessagesService
|
||||||
import com.slack.eithernet.ApiResult
|
import com.slack.eithernet.ApiResult
|
||||||
@@ -49,9 +49,7 @@ class LongPollRepositoryImpl(
|
|||||||
mode = mode,
|
mode = mode,
|
||||||
version = version
|
version = version
|
||||||
)
|
)
|
||||||
longPollService.getResponse(serverUrl, requestModel.map).mapResult(
|
|
||||||
successMapper = { response -> response },
|
longPollService.getResponse(serverUrl, requestModel.map).mapDefault()
|
||||||
errorMapper = { error -> error?.toDomain() }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core.common)
|
api(projects.core.common)
|
||||||
|
api(projects.core.ui)
|
||||||
|
|
||||||
implementation(libs.koin.android)
|
implementation(libs.koin.android)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.content.res.Resources
|
|||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.meloda.app.fast.datastore.model.LongPollState
|
import com.meloda.app.fast.datastore.model.LongPollState
|
||||||
import com.meloda.app.fast.datastore.model.ThemeConfig
|
import com.meloda.app.fast.ui.model.ThemeConfig
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
|||||||
@@ -36,16 +36,17 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// TODO: 05/05/2024, Danil Nikolaev: maybe remove
|
|
||||||
implementation(projects.core.common)
|
implementation(projects.core.common)
|
||||||
implementation(projects.core.datastore)
|
implementation(projects.core.datastore)
|
||||||
|
implementation(projects.core.ui)
|
||||||
|
|
||||||
implementation(libs.appcompat)
|
implementation(libs.appcompat)
|
||||||
implementation(libs.accompanist.permissions)
|
|
||||||
|
|
||||||
implementation(platform(libs.compose.bom))
|
implementation(platform(libs.compose.bom))
|
||||||
implementation(libs.bundles.compose)
|
implementation(libs.bundles.compose)
|
||||||
|
|
||||||
implementation(libs.haze)
|
implementation(libs.haze)
|
||||||
implementation(libs.haze.materials)
|
implementation(libs.haze.materials)
|
||||||
|
|
||||||
|
debugImplementation(libs.compose.ui.tooling)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,18 @@ import com.squareup.moshi.Json
|
|||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class SendSmsResponse(
|
data class ValidatePhoneResponse(
|
||||||
@Json(name = "sid") val validationSid: String?,
|
@Json(name = "sid") val validationSid: String?,
|
||||||
@Json(name = "delay") val delay: Int?,
|
@Json(name = "delay") val delay: Int?,
|
||||||
@Json(name = "validation_type") val validationType: String?,
|
@Json(name = "validation_type") val validationType: String?,
|
||||||
@Json(name = "validation_resend") val validationResend: String?
|
@Json(name = "validation_resend") val validationResend: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class ValidateLoginResponse(
|
||||||
|
@Json(name = "result") val result: String,
|
||||||
|
@Json(name = "sid") val sid: String,
|
||||||
|
@Json(name = "phone") val phone: String?,
|
||||||
|
@Json(name = "is_email") val isEmail: Boolean?,
|
||||||
|
@Json(name = "email_reg_allowed") val emailRegAllowed: Boolean?
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ package com.meloda.app.fast.model.api.responses
|
|||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class AuthDirectErrorOnlyResponse(
|
||||||
|
@Json(name = "error") val error: String,
|
||||||
|
@Json(name = "error_description") val errorDescription: String?,
|
||||||
|
@Json(name = "error_type") val errorType: String?
|
||||||
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class AuthDirectResponse(
|
data class AuthDirectResponse(
|
||||||
@Json(name = "access_token") val accessToken: String?,
|
@Json(name = "access_token") val accessToken: String?,
|
||||||
@@ -35,3 +42,9 @@ data class AuthDirectResponse(
|
|||||||
@Json(name = "restore_url") val restoreUrl: String
|
@Json(name = "restore_url") val restoreUrl: String
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class GetAnonymousTokenResponse(
|
||||||
|
@Json(name = "token") val token: String,
|
||||||
|
@Json(name = "expired_at") val expiredAt: Int
|
||||||
|
)
|
||||||
|
|||||||
@@ -32,37 +32,6 @@ fun <Success : Any, Error : Any, SuccessDomain : Any, ErrorDomain : Any>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Success : Any, Error : Any, SuccessDomain : Any, ErrorDomain : Any>
|
|
||||||
ApiResult<Success, Error>.mapOAuthResult(
|
|
||||||
successMapper: (Success) -> SuccessDomain,
|
|
||||||
errorMapper: (Error?) -> ErrorDomain?
|
|
||||||
): ApiResult<SuccessDomain, ErrorDomain> {
|
|
||||||
if (BuildConfig.DEBUG) printStackTraceIfAny()
|
|
||||||
|
|
||||||
return when (this) {
|
|
||||||
is ApiResult.Success -> {
|
|
||||||
ApiResult.success(successMapper(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
is ApiResult.Failure.NetworkFailure -> {
|
|
||||||
ApiResult.networkFailure(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ApiResult.Failure.UnknownFailure -> {
|
|
||||||
ApiResult.unknownFailure(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ApiResult.Failure.HttpFailure -> {
|
|
||||||
|
|
||||||
ApiResult.httpFailure(code, errorMapper(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
is ApiResult.Failure.ApiFailure -> {
|
|
||||||
ApiResult.apiFailure(errorMapper(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Success : ApiResponse<*>, SuccessDomain : Any, ErrorDomain : Any>
|
fun <Success : ApiResponse<*>, SuccessDomain : Any, ErrorDomain : Any>
|
||||||
ApiResult<Success, RestApiError>.mapApiResult(
|
ApiResult<Success, RestApiError>.mapApiResult(
|
||||||
successMapper: (Success) -> SuccessDomain,
|
successMapper: (Success) -> SuccessDomain,
|
||||||
@@ -86,30 +55,18 @@ fun <Success : ApiResponse<*>, SuccessDomain : Any, ErrorDomain : Any>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <R : Any> ApiResult<R, RestApiError>.mapDefault(): ApiResult<R, RestApiErrorDomain> =
|
||||||
|
mapResult(
|
||||||
|
successMapper = { response -> response },
|
||||||
|
errorMapper = { error -> error?.toDomain() }
|
||||||
|
)
|
||||||
|
|
||||||
fun <T : Any, R : ApiResponse<T>> ApiResult<R, RestApiError>.mapApiDefault(): ApiResult<T, RestApiErrorDomain> =
|
fun <T : Any, R : ApiResponse<T>> ApiResult<R, RestApiError>.mapApiDefault(): ApiResult<T, RestApiErrorDomain> =
|
||||||
mapResult(
|
mapResult(
|
||||||
successMapper = { response -> response.requireResponse() },
|
successMapper = { response -> response.requireResponse() },
|
||||||
errorMapper = { error -> error?.toDomain() }
|
errorMapper = { error -> error?.toDomain() }
|
||||||
)
|
)
|
||||||
|
|
||||||
//@OptIn(ExperimentalContracts::class)
|
|
||||||
//inline fun <R : Any, E : Any, C> OAuthResponse<R, E>.fold(
|
|
||||||
// onSuccess: (value: R) -> C,
|
|
||||||
// onFailure: (failure: E) -> C,
|
|
||||||
//): C {
|
|
||||||
// contract {
|
|
||||||
// callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
|
||||||
// callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
|
|
||||||
// }
|
|
||||||
// return when (this) {
|
|
||||||
// is OAuthResponse.Success -> onSuccess(response)
|
|
||||||
// is OAuthResponse.Error -> onFailure(error)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
fun <Success : Any, Error : Any> ApiResult<Success, Error>.isSuccess(): Boolean =
|
|
||||||
this is ApiResult.Success
|
|
||||||
|
|
||||||
fun <Success : Any, Error : Any> ApiResult<Success, Error>.printStackTraceIfAny() {
|
fun <Success : Any, Error : Any> ApiResult<Success, Error>.printStackTraceIfAny() {
|
||||||
val throwable = when (this) {
|
val throwable = when (this) {
|
||||||
is ApiResult.Failure.NetworkFailure -> error
|
is ApiResult.Failure.NetworkFailure -> error
|
||||||
@@ -118,9 +75,3 @@ fun <Success : Any, Error : Any> ApiResult<Success, Error>.printStackTraceIfAny(
|
|||||||
}
|
}
|
||||||
throwable?.printStackTrace()
|
throwable?.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ApiResult.Failure.HttpFailure<*>?.tryCastToRestErrorDomain() =
|
|
||||||
this?.error as? RestApiErrorDomain
|
|
||||||
|
|
||||||
fun ApiResult.Failure.ApiFailure<*>?.tryCastToRestErrorDomain() =
|
|
||||||
this?.error as? RestApiErrorDomain
|
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
package com.meloda.app.fast.network
|
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
|
|
||||||
// TODO: 09/05/2024, Danil Nikolaev: reimplement as sealed class
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
open class OAuthError(
|
|
||||||
@Json(name = "error") open val error: String,
|
|
||||||
@Json(name = "error_description") open val errorDescription: String?,
|
|
||||||
@Json(name = "error_type") open val errorType: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class ValidationRequiredError(
|
|
||||||
@Json(name = "error") override val error: String, // "need_validation"
|
|
||||||
@Json(name = "error_description") override val errorDescription: String, // "sms sent, use code param" if sms method; "use app code" if 2fa app
|
|
||||||
@Json(name = "validation_type") val validationType: String, // 2fa_app, 2sa_sms
|
|
||||||
@Json(name = "validation_sid") val validationSid: String,
|
|
||||||
@Json(name = "phone_mask") val phoneMask: String, // "+7 *** *** ** 50"
|
|
||||||
@Json(name = "redirect_uri") val redirectUri: String,
|
|
||||||
@Json(name = "validation_resend") val validationResend: String?, // Приходит, если для отправки кода нужно вызвать метод auth.validatePhone
|
|
||||||
@Json(name = "cant_get_code_open_restore") val restoreIfCannotGetCode: Boolean?
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = errorDescription,
|
|
||||||
errorType = null
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class CaptchaRequiredError(
|
|
||||||
@Json(name = "error") override val error: String, // "need_captcha"
|
|
||||||
@Json(name = "captcha_sid") val captchaSid: String,
|
|
||||||
@Json(name = "captcha_img") val captchaImage: String,
|
|
||||||
@Json(name = "captcha_ts") val captchaTs: Double?,
|
|
||||||
@Json(name = "captcha_ratio") val captchaRatio: Double?,
|
|
||||||
@Json(name = "captcha_track") val captchaTrack: String?,
|
|
||||||
@Json(name = "is_refresh_enabled") val isRefreshEnabled: Boolean?,
|
|
||||||
@Json(name = "is_sound_captcha_available") val isSoundCaptchaAvailable: Boolean?
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = null,
|
|
||||||
errorType = null
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class UserBannedError(
|
|
||||||
@Json(name = "error") override val error: String, // need_validation
|
|
||||||
@Json(name = "error_description") override val errorDescription: String, // user has been banned
|
|
||||||
@Json(name = "ban_info") val banInfo: BanInfo
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = errorDescription,
|
|
||||||
errorType = null
|
|
||||||
) {
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class BanInfo(
|
|
||||||
@Json(name = "member_name") val memberName: String,
|
|
||||||
@Json(name = "message") val message: String,
|
|
||||||
@Json(name = "access_token") val accessToken: String,
|
|
||||||
@Json(name = "restore_url") val restoreUrl: String
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class InvalidCredentialsError(
|
|
||||||
@Json(name = "error") override val error: String, // "invalid_client"
|
|
||||||
@Json(name = "error_description") override val errorDescription: String,
|
|
||||||
@Json(name = "error_type") override val errorType: String // "username_or_password_is_incorrect"
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = errorDescription,
|
|
||||||
errorType = errorType
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class WrongValidationCodeError(
|
|
||||||
@Json(name = "error") override val error: String, // "invalid_request"
|
|
||||||
@Json(name = "error_description") override val errorDescription: String,
|
|
||||||
@Json(name = "error_type") override val errorType: String // "wrong_otp"
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = errorDescription,
|
|
||||||
errorType = errorType
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class WrongValidationCodeFormatError(
|
|
||||||
@Json(name = "error") override val error: String, // "invalid_request"
|
|
||||||
@Json(name = "error_description") override val errorDescription: String,
|
|
||||||
@Json(name = "error_type") override val errorType: String // "otp_format_is_incorrect"
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = errorDescription,
|
|
||||||
errorType = errorType
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class TooManyTriesError(
|
|
||||||
@Json(name = "error") override val error: String, // "9;Flood control"
|
|
||||||
@Json(name = "error_description") override val errorDescription: String,
|
|
||||||
@Json(name = "error_type") override val errorType: String // "password_bruteforce_attempt"
|
|
||||||
) : OAuthError(
|
|
||||||
error = error,
|
|
||||||
errorDescription = errorDescription,
|
|
||||||
errorType = errorType
|
|
||||||
)
|
|
||||||
|
|
||||||
fun OAuthError.toDomain(): OAuthErrorDomain? = when (this) {
|
|
||||||
is ValidationRequiredError -> {
|
|
||||||
OAuthErrorDomain.ValidationRequiredError(
|
|
||||||
description = errorDescription,
|
|
||||||
validationType = ValidationType.parse(validationType),
|
|
||||||
validationSid = validationSid,
|
|
||||||
phoneMask = phoneMask,
|
|
||||||
redirectUri = redirectUri,
|
|
||||||
validationResend = validationResend,
|
|
||||||
restoreIfCannotGetCode = restoreIfCannotGetCode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is CaptchaRequiredError -> {
|
|
||||||
OAuthErrorDomain.CaptchaRequiredError(
|
|
||||||
captchaSid = captchaSid,
|
|
||||||
captchaImageUrl = captchaImage
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is UserBannedError -> {
|
|
||||||
OAuthErrorDomain.UserBannedError(
|
|
||||||
memberName = banInfo.memberName,
|
|
||||||
message = banInfo.message,
|
|
||||||
accessToken = banInfo.accessToken,
|
|
||||||
restoreUrl = banInfo.restoreUrl
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is InvalidCredentialsError -> {
|
|
||||||
OAuthErrorDomain.InvalidCredentialsError
|
|
||||||
}
|
|
||||||
|
|
||||||
is WrongValidationCodeError -> {
|
|
||||||
OAuthErrorDomain.WrongValidationCode
|
|
||||||
}
|
|
||||||
|
|
||||||
is WrongValidationCodeFormatError -> {
|
|
||||||
OAuthErrorDomain.WrongValidationCodeFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
is TooManyTriesError -> {
|
|
||||||
OAuthErrorDomain.TooManyTriesError
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,8 @@ package com.meloda.app.fast.network
|
|||||||
|
|
||||||
sealed class OAuthErrorDomain {
|
sealed class OAuthErrorDomain {
|
||||||
|
|
||||||
|
data object UnknownError : OAuthErrorDomain()
|
||||||
|
|
||||||
data class ValidationRequiredError(
|
data class ValidationRequiredError(
|
||||||
val description: String,
|
val description: String,
|
||||||
val validationType: ValidationType,
|
val validationType: ValidationType,
|
||||||
@@ -28,6 +30,4 @@ sealed class OAuthErrorDomain {
|
|||||||
data object WrongValidationCode : OAuthErrorDomain()
|
data object WrongValidationCode : OAuthErrorDomain()
|
||||||
data object WrongValidationCodeFormat : OAuthErrorDomain()
|
data object WrongValidationCodeFormat : OAuthErrorDomain()
|
||||||
data object TooManyTriesError: OAuthErrorDomain()
|
data object TooManyTriesError: OAuthErrorDomain()
|
||||||
|
|
||||||
data object UnknownError : OAuthErrorDomain()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.meloda.app.fast.network
|
package com.meloda.app.fast.network
|
||||||
|
|
||||||
sealed interface OAuthResponse<out R : Any, out E : Any> {
|
sealed interface OAuthResponse<out R : Any, out E : Any> {
|
||||||
|
|
||||||
data class Success<out R : Any>(val response: R) : OAuthResponse<R, Nothing>
|
data class Success<out R : Any>(val response: R) : OAuthResponse<R, Nothing>
|
||||||
|
|
||||||
data class Error<out E : Any>(val error: E?) : OAuthResponse<Nothing, E>
|
data class Error<out E : Any>(val error: E?) : OAuthResponse<Nothing, E>
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-93
@@ -1,6 +1,6 @@
|
|||||||
package com.meloda.app.fast.network
|
package com.meloda.app.fast.network
|
||||||
|
|
||||||
import android.util.Log
|
import com.meloda.app.fast.model.api.responses.AuthDirectErrorOnlyResponse
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okio.Timeout
|
import okio.Timeout
|
||||||
@@ -28,10 +28,10 @@ class OAuthResultCallFactory(private val moshi: Moshi) : CallAdapter.Factory() {
|
|||||||
if (getRawType(callInnerType) == OAuthResponse::class.java) {
|
if (getRawType(callInnerType) == OAuthResponse::class.java) {
|
||||||
if (callInnerType is ParameterizedType) {
|
if (callInnerType is ParameterizedType) {
|
||||||
val resultInnerType = getParameterUpperBound(0, callInnerType)
|
val resultInnerType = getParameterUpperBound(0, callInnerType)
|
||||||
return ResultCallAdapter<Any, OAuthError>(resultInnerType, moshi)
|
return ResultCallAdapter<Any>(resultInnerType, moshi)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCallAdapter<Nothing, Nothing>(Nothing::class.java, moshi)
|
return ResultCallAdapter<Nothing>(Nothing::class.java, moshi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,36 +60,36 @@ internal abstract class CallDelegate<In, Out>(protected val proxy: Call<In>) : C
|
|||||||
abstract fun cloneImpl(): Call<Out>
|
abstract fun cloneImpl(): Call<Out>
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResultCallAdapter<R : Any, E : OAuthError>(
|
private class ResultCallAdapter<R : Any>(
|
||||||
private val type: Type,
|
private val type: Type,
|
||||||
private val moshi: Moshi
|
private val moshi: Moshi
|
||||||
) : CallAdapter<R, Call<OAuthResponse<R, E>>> {
|
) : CallAdapter<R, Call<OAuthResponse<R, AuthDirectErrorOnlyResponse>>> {
|
||||||
|
|
||||||
override fun responseType() = type
|
override fun responseType() = type
|
||||||
|
|
||||||
override fun adapt(call: Call<R>): Call<OAuthResponse<R, E>> = ResultCall(call, moshi)
|
override fun adapt(call: Call<R>): Call<OAuthResponse<R, AuthDirectErrorOnlyResponse>> =
|
||||||
|
ResultCall(call, moshi)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ResultCall<R : Any, E : OAuthError>(
|
internal class ResultCall<R : Any>(
|
||||||
proxy: Call<R>,
|
proxy: Call<R>,
|
||||||
private val moshi: Moshi
|
private val moshi: Moshi
|
||||||
) : CallDelegate<R, OAuthResponse<R, E>>(proxy) {
|
) : CallDelegate<R, OAuthResponse<R, AuthDirectErrorOnlyResponse>>(proxy) {
|
||||||
|
|
||||||
override fun enqueueImpl(callback: Callback<OAuthResponse<R, E>>) {
|
override fun enqueueImpl(callback: Callback<OAuthResponse<R, AuthDirectErrorOnlyResponse>>) {
|
||||||
proxy.enqueue(ResultCallback(this, callback, moshi))
|
proxy.enqueue(ResultCallback(this, callback, moshi))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cloneImpl(): ResultCall<R, E> {
|
override fun cloneImpl(): ResultCall<R> {
|
||||||
return ResultCall(proxy.clone(), moshi)
|
return ResultCall(proxy.clone(), moshi)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResultCallback<R : Any, E : OAuthError>(
|
private class ResultCallback<R : Any>(
|
||||||
private val proxy: ResultCall<R, E>,
|
private val proxy: ResultCall<R>,
|
||||||
private val callback: Callback<OAuthResponse<R, E>>,
|
private val callback: Callback<OAuthResponse<R, AuthDirectErrorOnlyResponse>>,
|
||||||
private val moshi: Moshi
|
private val moshi: Moshi
|
||||||
) : Callback<R> {
|
) : Callback<R> {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun onResponse(call: Call<R>, response: Response<R>) {
|
override fun onResponse(call: Call<R>, response: Response<R>) {
|
||||||
when {
|
when {
|
||||||
response.isSuccessful -> {
|
response.isSuccessful -> {
|
||||||
@@ -106,92 +106,19 @@ internal class ResultCall<R : Any, E : OAuthError>(
|
|||||||
else -> {
|
else -> {
|
||||||
val errorBodyString = response.errorBody()?.string()
|
val errorBodyString = response.errorBody()?.string()
|
||||||
|
|
||||||
val baseError: OAuthError = moshi.adapter(OAuthError::class.java)
|
val baseError = moshi.adapter(AuthDirectErrorOnlyResponse::class.java)
|
||||||
.fromJson(errorBodyString.orEmpty()) ?: return
|
.fromJson(errorBodyString.orEmpty()) ?: return
|
||||||
|
|
||||||
val error: OAuthError? = when (baseError.error) {
|
callback.onResponse(
|
||||||
"9;Flood control" -> {
|
proxy,
|
||||||
moshi.adapter(TooManyTriesError::class.java)
|
Response.success(OAuthResponse.Error(baseError) as OAuthResponse<R, AuthDirectErrorOnlyResponse>)
|
||||||
.fromJson(errorBodyString.orEmpty())
|
)
|
||||||
}
|
|
||||||
|
|
||||||
"invalid_client" -> {
|
|
||||||
moshi.adapter(InvalidCredentialsError::class.java)
|
|
||||||
.fromJson(errorBodyString.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
"need_captcha" -> {
|
|
||||||
moshi.adapter(CaptchaRequiredError::class.java)
|
|
||||||
.fromJson(errorBodyString.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
"invalid_request" -> {
|
|
||||||
when (val type = baseError.errorType) {
|
|
||||||
"wrong_otp" -> {
|
|
||||||
moshi.adapter(WrongValidationCodeError::class.java)
|
|
||||||
.fromJson(errorBodyString.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
"otp_format_is_incorrect" -> {
|
|
||||||
moshi.adapter(WrongValidationCodeFormatError::class.java)
|
|
||||||
.fromJson(errorBodyString.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
Log.d(
|
|
||||||
"ResultCallback",
|
|
||||||
"onResponse: invalid_request; error_type: $type"
|
|
||||||
)
|
|
||||||
|
|
||||||
error("Unknown type: $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"need_validation" -> {
|
|
||||||
when (val description = baseError.errorDescription) {
|
|
||||||
"user has been banned" -> {
|
|
||||||
moshi.adapter(UserBannedError::class.java)
|
|
||||||
.fromJson(errorBodyString.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
"sms sent, use code param",
|
|
||||||
"use app code" -> {
|
|
||||||
moshi.adapter(ValidationRequiredError::class.java)
|
|
||||||
.fromJson(errorBodyString.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
Log.d(
|
|
||||||
"ResultCallback",
|
|
||||||
"onResponse: need_validation; description: $description"
|
|
||||||
)
|
|
||||||
|
|
||||||
error("Unknown description: $description")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
error?.let {
|
|
||||||
callback.onResponse(
|
|
||||||
proxy,
|
|
||||||
Response.success(OAuthResponse.Error(error) as OAuthResponse<R, E>)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<R>, error: Throwable) {
|
override fun onFailure(call: Call<R>, error: Throwable) {
|
||||||
val b = error
|
throw error
|
||||||
// TODO: 12/04/2024, Danil Nikolaev: handle
|
|
||||||
// callback.onResponse(
|
|
||||||
// proxy,
|
|
||||||
// Response.success(OAuthAnswer.Error((throwable = error)))
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.meloda.app.fast.network;
|
package com.meloda.app.fast.network;
|
||||||
|
|
||||||
enum class ValidationType(val value: String) {
|
enum class ValidationType(val value: String) {
|
||||||
APP("2fa_app"), SMS("2fa_sms");
|
APP("2fa_app"),
|
||||||
|
SMS("2fa_sms");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(value: String): ValidationType = entries.first { it.value == value }
|
fun parse(value: String): ValidationType = entries.first { it.value == value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.meloda.app.fast.network
|
||||||
|
|
||||||
|
enum class VkErrorCode(val code: Int) {
|
||||||
|
UNKNOWN_ERROR(1),
|
||||||
|
APP_DISABLED(2),
|
||||||
|
UNKNOWN_METHOD(3),
|
||||||
|
INVALID_SIGNATURE(4),
|
||||||
|
USER_AUTHORIZATION_FAILED(5),
|
||||||
|
TOO_MANY_REQUESTS(6),
|
||||||
|
NO_RIGHTS(7),
|
||||||
|
BAD_REQUEST(8),
|
||||||
|
TOO_MANY_SIMILAR_ACTIONS(9),
|
||||||
|
INTERNAL_SERVER_ERROR(10),
|
||||||
|
IN_TEST_MODE(11),
|
||||||
|
EXECUTE_CODE_COMPILE_ERROR(12),
|
||||||
|
EXECUTE_CODE_RUNTIME_ERROR(13),
|
||||||
|
CAPTCHA_NEEDED(14),
|
||||||
|
ACCESS_DENIED(15),
|
||||||
|
REQUIRES_REQUESTS_OVER_HTTPS(16),
|
||||||
|
VALIDATION_REQUIRED(17),
|
||||||
|
USER_BANNED_OR_DELETED(18),
|
||||||
|
ACTION_PROHIBITED(20),
|
||||||
|
ACTION_ALLOWED_ONLY_FOR_STANDALONE(21),
|
||||||
|
METHOD_OFF(23),
|
||||||
|
CONFIRMATION_REQUIRED(24),
|
||||||
|
PARAMETER_IS_NOT_SPECIFIED(100),
|
||||||
|
INCORRECT_APP_ID(101),
|
||||||
|
OUT_OF_LIMITS(103),
|
||||||
|
INCORRECT_USER_ID(113),
|
||||||
|
INCORRECT_TIMESTAMP(150),
|
||||||
|
ACCESS_TO_ALBUM_DENIED(200),
|
||||||
|
ACCESS_TO_AUDIO_DENIED(201),
|
||||||
|
ACCESS_TO_GROUP_DENIED(203),
|
||||||
|
ALBUM_IS_FULL(300),
|
||||||
|
ACTION_DENIED(500),
|
||||||
|
PERMISSION_DENIED(600),
|
||||||
|
CANNOT_SEND_MESSAGE_BLACK_LIST(900),
|
||||||
|
CANNOT_SEND_MESSAGE_GROUP(901),
|
||||||
|
INVALID_DOC_ID(1150),
|
||||||
|
INVALID_DOC_TITLE(1152),
|
||||||
|
ACCESS_TO_DOC_DENIED(1153),
|
||||||
|
|
||||||
|
SOME_AUTH_ERROR(104),
|
||||||
|
ACCESS_TOKEN_EXPIRED(1117);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun parse(code: Int): VkErrorCode = entries.firstOrNull { it.code == code }
|
||||||
|
?: throw IllegalArgumentException("Unknown error with value: $code")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package com.meloda.app.fast.network
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
object VkErrorCodes {
|
|
||||||
const val UnknownError = 1
|
|
||||||
const val AppDisabled = 2
|
|
||||||
const val UnknownMethod = 3
|
|
||||||
const val InvalidSignature = 4
|
|
||||||
const val UserAuthorizationFailed = 5
|
|
||||||
const val TooManyRequests = 6
|
|
||||||
const val NoRights = 7
|
|
||||||
const val BadRequest = 8
|
|
||||||
const val TooManySimilarActions = 9
|
|
||||||
const val InternalServerError = 10
|
|
||||||
const val InTestMode = 11
|
|
||||||
const val ExecuteCodeCompileError = 12
|
|
||||||
const val ExecuteCodeRuntimeError = 13
|
|
||||||
const val CaptchaNeeded = 14
|
|
||||||
const val AccessDenied = 15
|
|
||||||
const val RequiresRequestsOverHttps = 16
|
|
||||||
const val ValidationRequired = 17
|
|
||||||
const val UserBannedOrDeleted = 18
|
|
||||||
const val ActionProhibited = 20
|
|
||||||
const val ActionAllowedOnlyForStandalone = 21
|
|
||||||
const val MethodOff = 23
|
|
||||||
const val ConfirmationRequired = 24
|
|
||||||
const val ParameterIsNotSpecified = 100
|
|
||||||
const val IncorrectAppId = 101
|
|
||||||
const val OutOfLimits = 103
|
|
||||||
const val IncorrectUserId = 113
|
|
||||||
const val IncorrectTimestamp = 150
|
|
||||||
const val AccessToAlbumDenied = 200
|
|
||||||
const val AccessToAudioDenied = 201
|
|
||||||
const val AccessToGroupDenied = 203
|
|
||||||
const val AlbumIsFull = 300
|
|
||||||
const val ActionDenied = 500
|
|
||||||
const val PermissionDenied = 600
|
|
||||||
const val CannotSendMessageBlackList = 900
|
|
||||||
const val CannotSendMessageGroup = 901
|
|
||||||
const val InvalidDocId = 1150
|
|
||||||
const val InvalidDocTitle = 1152
|
|
||||||
const val AccessToDocDenied = 1153
|
|
||||||
|
|
||||||
const val AccessTokenExpired = 1117
|
|
||||||
}
|
|
||||||
|
|
||||||
object VkOAuthErrors {
|
|
||||||
const val UNKNOWN = "unknown_error"
|
|
||||||
|
|
||||||
const val NEED_VALIDATION = "need_validation"
|
|
||||||
const val NEED_CAPTCHA = "need_captcha"
|
|
||||||
const val INVALID_CLIENT = "invalid_client"
|
|
||||||
const val INVALID_REQUEST = "invalid_request"
|
|
||||||
const val FLOOD_CONTROL = "9;Flood control"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object VkErrorTypes {
|
|
||||||
const val WRONG_OTP_FORMAT = "otp_format_is_incorrect"
|
|
||||||
const val WRONG_OTP = "wrong_otp"
|
|
||||||
const val PASSWORD_BRUTEFORCE_ATTEMPT = "password_bruteforce_attempt"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.meloda.app.fast.network
|
||||||
|
|
||||||
|
enum class VkOAuthError(val value: String) {
|
||||||
|
UNKNOWN("unknown_error"),
|
||||||
|
|
||||||
|
NEED_VALIDATION("need_validation"),
|
||||||
|
NEED_CAPTCHA("need_captcha"),
|
||||||
|
INVALID_CLIENT("invalid_client"),
|
||||||
|
INVALID_REQUEST("invalid_request"),
|
||||||
|
FLOOD_CONTROL("9;Flood control");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun parse(value: String): VkOAuthError = entries.firstOrNull { it.value == value }
|
||||||
|
?: throw IllegalArgumentException("Unknown error with value: $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.meloda.app.fast.network;
|
||||||
|
|
||||||
|
enum class VkOAuthErrorType(val value: String) {
|
||||||
|
WRONG_OTP_FORMAT("otp_format_is_incorrect"),
|
||||||
|
WRONG_OTP("wrong_otp"),
|
||||||
|
PASSWORD_BRUTEFORCE_ATTEMPT("password_bruteforce_attempt");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun parse(value: String): VkOAuthErrorType = entries.firstOrNull { it.value == value }
|
||||||
|
?: throw IllegalArgumentException("Unknown error type with value: $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-3
@@ -1,14 +1,23 @@
|
|||||||
package com.meloda.app.fast.network.service.auth
|
package com.meloda.app.fast.network.service.auth
|
||||||
|
|
||||||
import com.meloda.app.fast.model.api.responses.SendSmsResponse
|
import com.meloda.app.fast.model.api.responses.ValidateLoginResponse
|
||||||
|
import com.meloda.app.fast.model.api.responses.ValidatePhoneResponse
|
||||||
import com.meloda.app.fast.network.ApiResponse
|
import com.meloda.app.fast.network.ApiResponse
|
||||||
import com.meloda.app.fast.network.RestApiError
|
import com.meloda.app.fast.network.RestApiError
|
||||||
import com.slack.eithernet.ApiResult
|
import com.slack.eithernet.ApiResult
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
import retrofit2.http.QueryMap
|
||||||
|
|
||||||
interface AuthService {
|
interface AuthService {
|
||||||
|
|
||||||
@GET(AuthUrls.SEND_SMS)
|
@GET(AuthUrls.VALIDATE_PHONE)
|
||||||
suspend fun sendSms(@Query("sid") validationSid: String): ApiResult<ApiResponse<SendSmsResponse>, RestApiError>
|
suspend fun validatePhone(
|
||||||
|
@Query("sid") validationSid: String
|
||||||
|
): ApiResult<ApiResponse<ValidatePhoneResponse>, RestApiError>
|
||||||
|
|
||||||
|
@GET(AuthUrls.VALIDATE_LOGIN)
|
||||||
|
suspend fun validateLogin(
|
||||||
|
@QueryMap param: Map<String, String>
|
||||||
|
): ApiResult<ApiResponse<ValidateLoginResponse>, RestApiError>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.meloda.app.fast.network.service.auth
|
|||||||
import com.meloda.app.fast.common.AppConstants
|
import com.meloda.app.fast.common.AppConstants
|
||||||
|
|
||||||
object AuthUrls {
|
object AuthUrls {
|
||||||
|
private const val URL = AppConstants.URL_API
|
||||||
|
|
||||||
const val SEND_SMS = "${AppConstants.URL_API}/auth.validatePhone"
|
const val VALIDATE_PHONE = "$URL/auth.validatePhone"
|
||||||
|
const val VALIDATE_LOGIN = "$URL/auth.validateLogin"
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -1,6 +1,7 @@
|
|||||||
package com.meloda.app.fast.network.service.oauth
|
package com.meloda.app.fast.network.service.oauth
|
||||||
|
|
||||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
||||||
|
import com.meloda.app.fast.model.api.responses.GetAnonymousTokenResponse
|
||||||
import com.slack.eithernet.ApiResult
|
import com.slack.eithernet.ApiResult
|
||||||
import com.slack.eithernet.DecodeErrorBody
|
import com.slack.eithernet.DecodeErrorBody
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
@@ -11,6 +12,12 @@ interface OAuthService {
|
|||||||
@DecodeErrorBody
|
@DecodeErrorBody
|
||||||
@GET(OAuthUrls.DIRECT_AUTH)
|
@GET(OAuthUrls.DIRECT_AUTH)
|
||||||
suspend fun auth(
|
suspend fun auth(
|
||||||
@QueryMap param: Map<String, String?>
|
@QueryMap param: Map<String, String>
|
||||||
): ApiResult<AuthDirectResponse, AuthDirectResponse>
|
): ApiResult<AuthDirectResponse, AuthDirectResponse>
|
||||||
|
|
||||||
|
@DecodeErrorBody
|
||||||
|
@GET(OAuthUrls.GET_ANONYMOUS_TOKEN)
|
||||||
|
suspend fun getAnonymousToken(
|
||||||
|
@QueryMap param: Map<String, String>
|
||||||
|
): ApiResult<GetAnonymousTokenResponse, GetAnonymousTokenResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.meloda.app.fast.network.service.oauth
|
|||||||
import com.meloda.app.fast.common.AppConstants
|
import com.meloda.app.fast.common.AppConstants
|
||||||
|
|
||||||
object OAuthUrls {
|
object OAuthUrls {
|
||||||
|
private const val URL = AppConstants.URL_OAUTH
|
||||||
|
|
||||||
const val DIRECT_AUTH = "${AppConstants.URL_OAUTH}/token"
|
const val DIRECT_AUTH = "$URL/token"
|
||||||
|
const val GET_ANONYMOUS_TOKEN = "$URL/get_anonym_token"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,14 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core.designsystem)
|
implementation(projects.core.common)
|
||||||
api(projects.core.model)
|
api(projects.core.model)
|
||||||
|
|
||||||
|
implementation(libs.haze)
|
||||||
|
implementation(libs.haze.materials)
|
||||||
|
|
||||||
implementation(platform(libs.compose.bom))
|
implementation(platform(libs.compose.bom))
|
||||||
implementation(libs.bundles.compose)
|
implementation(libs.bundles.compose)
|
||||||
|
|
||||||
|
debugImplementation(libs.compose.ui.tooling)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.basic
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
+2
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.basic
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 The Android Open Source Project
|
* Copyright 2020 The Android Open Source Project
|
||||||
@@ -22,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.ui.graphics.luminance
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
import com.meloda.app.fast.ui.util.isUsingDarkTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default alpha levels used by Material components.
|
* Default alpha levels used by Material components.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.basic
|
||||||
|
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.conversations
|
package com.meloda.app.fast.ui.components
|
||||||
|
|
||||||
// TODO: 26.08.2023, Danil Nikolaev: rewrite
|
// TODO: 26.08.2023, Danil Nikolaev: rewrite
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ fun DotsCollision() {
|
|||||||
animationSpec = infiniteRepeatable(animation = keyframes {
|
animationSpec = infiniteRepeatable(animation = keyframes {
|
||||||
durationMillis = delayUnit * 3
|
durationMillis = delayUnit * 3
|
||||||
0f at 0 using LinearEasing
|
0f at 0 using LinearEasing
|
||||||
-maxOffset at delayUnit / 2 using LinearEasing
|
maxOffset at delayUnit / 2 using LinearEasing
|
||||||
0f at delayUnit
|
0f at delayUnit
|
||||||
}), label = ""
|
}), label = ""
|
||||||
)
|
)
|
||||||
+11
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.ui
|
package com.meloda.app.fast.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -11,6 +11,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -40,3 +41,12 @@ fun ErrorView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ErrorViewPreview() {
|
||||||
|
ErrorView(
|
||||||
|
text = "Some error occurred",
|
||||||
|
buttonText = "Restart"
|
||||||
|
)
|
||||||
|
}
|
||||||
+8
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem.components
|
package com.meloda.app.fast.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -7,6 +7,7 @@ import androidx.compose.material3.CircularProgressIndicator
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FullScreenLoader(modifier: Modifier = Modifier) {
|
fun FullScreenLoader(modifier: Modifier = Modifier) {
|
||||||
@@ -19,3 +20,9 @@ fun FullScreenLoader(modifier: Modifier = Modifier) {
|
|||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun FullScreenLoaderPreview() {
|
||||||
|
FullScreenLoader()
|
||||||
|
}
|
||||||
+7
-4
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -35,7 +35,9 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.meloda.app.fast.common.UiText
|
import com.meloda.app.fast.common.UiText
|
||||||
import com.meloda.app.fast.common.parseString
|
import com.meloda.app.fast.common.parseString
|
||||||
import com.meloda.app.fast.designsystem.ImmutableList.Companion.toImmutableList
|
import com.meloda.app.fast.ui.util.ImmutableList
|
||||||
|
import com.meloda.app.fast.ui.util.ImmutableList.Companion.toImmutableList
|
||||||
|
import com.meloda.app.fast.ui.util.getString
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -239,7 +241,7 @@ fun MaterialDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 08.04.2023, Danil Nikolaev: refactor this
|
// TODO: 08.04.2023, Danil Nikolaev: remove this
|
||||||
@Deprecated("need refactoring")
|
@Deprecated("need refactoring")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -452,8 +454,9 @@ fun MaterialDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AlertItems(
|
fun AlertItems(
|
||||||
selectionType: ItemsSelectionType,
|
selectionType: ItemsSelectionType,
|
||||||
items: ImmutableList<DialogItem>,
|
items: ImmutableList<DialogItem>,
|
||||||
onItemClick: ((index: Int) -> Unit)? = null,
|
onItemClick: ((index: Int) -> Unit)? = null,
|
||||||
+11
-2
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem.components
|
package com.meloda.app.fast.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -8,7 +8,8 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.meloda.app.fast.designsystem.R
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.meloda.app.fast.ui.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NoItemsView(
|
fun NoItemsView(
|
||||||
@@ -25,3 +26,11 @@ fun NoItemsView(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun NoItemsViewPreview() {
|
||||||
|
NoItemsView(
|
||||||
|
customText = "Nothing here..."
|
||||||
|
)
|
||||||
|
}
|
||||||
+8
-2
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@@ -10,8 +10,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.meloda.app.fast.ui.R
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TextFieldErrorText(
|
fun TextFieldErrorText(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -30,3 +30,9 @@ fun TextFieldErrorText(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun TextFieldErrorPreview() {
|
||||||
|
TextFieldErrorText(text = "Error")
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.model
|
||||||
|
|
||||||
data class TabItem(
|
data class TabItem(
|
||||||
val titleResId: Int?,
|
val titleResId: Int?,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.datastore.model
|
package com.meloda.app.fast.ui.model
|
||||||
|
|
||||||
data class ThemeConfig(
|
data class ThemeConfig(
|
||||||
val usingDarkStyle: Boolean,
|
val usingDarkStyle: Boolean,
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.theme
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -20,8 +20,8 @@ import androidx.compose.ui.text.font.FontStyle
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import com.meloda.app.fast.datastore.model.ThemeConfig
|
import com.meloda.app.fast.ui.R
|
||||||
import com.meloda.app.fast.designsystem.colorschemes.ClassicColorScheme
|
import com.meloda.app.fast.ui.model.ThemeConfig
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
|
|
||||||
private val googleSansFonts = FontFamily(
|
private val googleSansFonts = FontFamily(
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem.colorschemes
|
package com.meloda.app.fast.ui.theme
|
||||||
|
|
||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
+11
-11
@@ -1,8 +1,7 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.util
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
@@ -17,17 +16,17 @@ import androidx.compose.ui.res.pluralStringResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.meloda.app.fast.common.UiText
|
import com.meloda.app.fast.common.UiText
|
||||||
import com.meloda.app.fast.common.util.AndroidUtils
|
import com.meloda.app.fast.common.util.AndroidUtils
|
||||||
import com.meloda.app.fast.datastore.SettingsController
|
|
||||||
import com.meloda.app.fast.datastore.SettingsKeys
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun isUsingDarkTheme(): Boolean {
|
fun isUsingDarkTheme(): Boolean {
|
||||||
val nightThemeMode = SettingsController.getInt(
|
// val nightThemeMode = AppCompatDelegate.MODE_NIGHT_YES
|
||||||
SettingsKeys.KEY_APPEARANCE_DARK_THEME,
|
|
||||||
SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_THEME
|
// SettingsController.getInt(
|
||||||
)
|
// SettingsKeys.KEY_APPEARANCE_DARK_THEME,
|
||||||
val appForceDarkMode = nightThemeMode == AppCompatDelegate.MODE_NIGHT_YES
|
// SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_THEME
|
||||||
val appBatterySaver = nightThemeMode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
|
// )
|
||||||
|
// val appForceDarkMode = nightThemeMode == AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
// val appBatterySaver = nightThemeMode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
@@ -37,7 +36,8 @@ fun isUsingDarkTheme(): Boolean {
|
|||||||
val isSystemUsingDarkTheme =
|
val isSystemUsingDarkTheme =
|
||||||
systemUiNightMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
systemUiNightMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||||
|
|
||||||
return appForceDarkMode || (appBatterySaver && isSystemBatterySaver) || (!appBatterySaver && isSystemUsingDarkTheme && nightThemeMode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
return true
|
||||||
|
// return appForceDarkMode || (appBatterySaver && isSystemBatterySaver) || (!appBatterySaver && isSystemUsingDarkTheme && nightThemeMode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.meloda.app.fast.designsystem
|
package com.meloda.app.fast.ui.util
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user