forked from melod1n/fast-messenger
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 797e966b65 | |||
| 2c8536a9da | |||
| 12ba4faade | |||
| 89e5d10bdf | |||
| 79e5823ff8 | |||
| 6995fc385c | |||
| bed1348d19 | |||
| 7fa2da8a2c | |||
| 842ffe9f5b | |||
| 72520863bd | |||
| b69273b043 | |||
| 81f9742120 | |||
| a8cd647d04 | |||
| 7e17738024 | |||
| 9983fa3752 | |||
| 5fd2fb0abb | |||
| 59280a0358 | |||
| e9f84cbdf4 | |||
| b80fc6c936 | |||
| 92a156f2f0 | |||
| a20b4e42ad | |||
| 124033fb9c | |||
| 11c394cc9f | |||
| 60d173e1f3 | |||
| e1a2cce08d | |||
| 9756a36650 | |||
| f363e2c547 | |||
| 1cddcd7e99 | |||
| 095aa20dcc | |||
| ce5a43aea9 | |||
| 8824775f68 | |||
| 7e5843759d | |||
| 7c14df1824 |
@@ -7,10 +7,10 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "dev.meloda.fast"
|
namespace = "dev.meloda.fastvk"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "dev.meloda.fast"
|
applicationId = "dev.meloda.fastvk"
|
||||||
|
|
||||||
versionCode = libs.versions.versionCode.get().toInt()
|
versionCode = libs.versions.versionCode.get().toInt()
|
||||||
versionName = libs.versions.versionName.get()
|
versionName = libs.versions.versionName.get()
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
tools:targetApi="tiramisu">
|
tools:targetApi="tiramisu">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".presentation.MainActivity"
|
android:name="dev.meloda.fast.presentation.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
|
||||||
@@ -38,13 +38,13 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.longpolling.LongPollingService"
|
android:name="dev.meloda.fast.service.longpolling.LongPollingService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.OnlineService"
|
android:name="dev.meloda.fast.service.OnlineService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ class MainViewModelImpl(
|
|||||||
BaseError.SessionExpired -> {
|
BaseError.SessionExpired -> {
|
||||||
isNeedToReplaceWithAuth.update { true }
|
isNeedToReplaceWithAuth.update { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is BaseError.SimpleError -> Unit // TODO: 21-Mar-25, Danil Nikolaev: show error in ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import dev.meloda.fast.provider.ApiLanguageProvider
|
|||||||
import dev.meloda.fast.service.longpolling.di.longPollModule
|
import dev.meloda.fast.service.longpolling.di.longPollModule
|
||||||
import dev.meloda.fast.settings.di.settingsModule
|
import dev.meloda.fast.settings.di.settingsModule
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModelOf
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
|
import org.koin.core.module.dsl.viewModelOf
|
||||||
import org.koin.core.qualifier.qualifier
|
import org.koin.core.qualifier.qualifier
|
||||||
import org.koin.dsl.bind
|
import org.koin.dsl.bind
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
@@ -61,7 +61,7 @@ val applicationModule = module {
|
|||||||
qualifier = qualifier("main")
|
qualifier = qualifier("main")
|
||||||
}
|
}
|
||||||
|
|
||||||
single {
|
single<ImageLoader> {
|
||||||
ImageLoader.Builder(get())
|
ImageLoader.Builder(get())
|
||||||
.crossfade(true)
|
.crossfade(true)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -95,11 +95,6 @@ class OnlineService : Service() {
|
|||||||
}.also { coroutine -> coroutine.invokeOnCompletion { onlineJob = null } }
|
}.also { coroutine -> coroutine.invokeOnCompletion { onlineJob = null } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLowMemory() {
|
|
||||||
Log.d(STATE_TAG, "onLowMemory")
|
|
||||||
super.onLowMemory()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(STATE_TAG, "onDestroy")
|
Log.d(STATE_TAG, "onDestroy")
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package dev.meloda.fast.common.extensions
|
package dev.meloda.fast.common.extensions
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ object AndroidUtils {
|
|||||||
action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
Settings.ACTION_SECURITY_SETTINGS
|
Settings.ACTION_SECURITY_SETTINGS
|
||||||
} else {
|
} else {
|
||||||
data = Uri.parse("package:dev.meloda.fast")
|
data = Uri.parse("package:dev.meloda.fastvk")
|
||||||
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES
|
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,18 +20,20 @@ sealed class State<out T> {
|
|||||||
|
|
||||||
data object ConnectionError : Error()
|
data object ConnectionError : Error()
|
||||||
|
|
||||||
data object Unknown : Error()
|
data object UnknownError : Error()
|
||||||
|
|
||||||
data object InternalError : Error()
|
data object InternalError : Error()
|
||||||
|
|
||||||
data class OAuthError(val error: OAuthErrorDomain) : Error()
|
data class OAuthError(val error: OAuthErrorDomain) : Error()
|
||||||
|
|
||||||
|
data class TestError(val message: String) : Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isLoading(): Boolean = this is Loading
|
fun isLoading(): Boolean = this is Loading
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val UNKNOWN_ERROR = Error.Unknown
|
val UNKNOWN_ERROR = Error.UnknownError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +75,12 @@ fun <T : Any> ApiResult<T, RestApiErrorDomain>.mapToState() = when (this) {
|
|||||||
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
|
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any, N> ApiResult<T, RestApiErrorDomain>.mapToState(successMapper: (T) -> N) = when (this) {
|
fun <T : Any, N> ApiResult<T, RestApiErrorDomain>.mapToState(successMapper: (T) -> N) =
|
||||||
|
when (this) {
|
||||||
is ApiResult.Success -> State.Success(successMapper(this.value))
|
is ApiResult.Success -> State.Success(successMapper(this.value))
|
||||||
|
|
||||||
is ApiResult.Failure.NetworkFailure -> State.Error.ConnectionError
|
is ApiResult.Failure.NetworkFailure -> State.Error.ConnectionError
|
||||||
is ApiResult.Failure.UnknownFailure -> State.UNKNOWN_ERROR
|
is ApiResult.Failure.UnknownFailure -> State.UNKNOWN_ERROR
|
||||||
is ApiResult.Failure.HttpFailure -> this.error.toStateApiError()
|
is ApiResult.Failure.HttpFailure -> this.error.toStateApiError()
|
||||||
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
|
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class OAuthUseCaseImpl(
|
|||||||
forceSms = forceSms
|
forceSms = forceSms
|
||||||
)
|
)
|
||||||
|
|
||||||
|
kotlin.runCatching {
|
||||||
val error = response.error?.let(VkOAuthError::parse)
|
val error = response.error?.let(VkOAuthError::parse)
|
||||||
val errorType = response.errorType?.let(VkOAuthErrorType::parse)
|
val errorType = response.errorType?.let(VkOAuthErrorType::parse)
|
||||||
|
|
||||||
@@ -120,5 +121,12 @@ class OAuthUseCaseImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit(newState)
|
emit(newState)
|
||||||
|
}.fold(
|
||||||
|
onSuccess = {
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
emit(State.Error.TestError(it.stackTraceToString()))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ import androidx.compose.runtime.Immutable
|
|||||||
sealed class BaseError {
|
sealed class BaseError {
|
||||||
|
|
||||||
data object SessionExpired : BaseError()
|
data object SessionExpired : BaseError()
|
||||||
|
|
||||||
|
data class SimpleError(val message: String) : BaseError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package dev.meloda.fast.model.api.data
|
package dev.meloda.fast.model.api.data
|
||||||
|
|
||||||
import dev.meloda.fast.model.api.domain.VkVideoDomain
|
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import dev.meloda.fast.model.api.domain.VkVideoDomain
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class VkVideoData(
|
data class VkVideoData(
|
||||||
@@ -12,7 +12,7 @@ data class VkVideoData(
|
|||||||
val duration: Int,
|
val duration: Int,
|
||||||
val date: Int,
|
val date: Int,
|
||||||
val comments: Int?,
|
val comments: Int?,
|
||||||
val description: String,
|
val description: String?,
|
||||||
val player: String?,
|
val player: String?,
|
||||||
val added: Int?,
|
val added: Int?,
|
||||||
val type: String,
|
val type: String,
|
||||||
@@ -20,9 +20,9 @@ data class VkVideoData(
|
|||||||
val access_key: String?,
|
val access_key: String?,
|
||||||
val owner_id: Int,
|
val owner_id: Int,
|
||||||
val is_favorite: Boolean?,
|
val is_favorite: Boolean?,
|
||||||
val image: List<Image>,
|
val image: List<Image>?,
|
||||||
val first_frame: List<FirstFrame>?,
|
val first_frame: List<FirstFrame>?,
|
||||||
val files: File?,
|
val files: File?
|
||||||
) : VkAttachmentData {
|
) : VkAttachmentData {
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
@@ -67,7 +67,7 @@ data class VkVideoData(
|
|||||||
fun toDomain() = VkVideoDomain(
|
fun toDomain() = VkVideoDomain(
|
||||||
id = id,
|
id = id,
|
||||||
ownerId = owner_id,
|
ownerId = owner_id,
|
||||||
images = image.map { it.asVideoImage() },
|
images = image.orEmpty().map { it.asVideoImage() },
|
||||||
firstFrames = first_frame,
|
firstFrames = first_frame,
|
||||||
accessKey = access_key,
|
accessKey = access_key,
|
||||||
title = title
|
title = title
|
||||||
|
|||||||
@@ -46,8 +46,13 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter
|
|||||||
return successModel
|
return successModel
|
||||||
},
|
},
|
||||||
onFailure = { failure ->
|
onFailure = { failure ->
|
||||||
if(failure is JsonDataException) {
|
if (failure is JsonDataException) {
|
||||||
throw failure
|
throw ApiException(
|
||||||
|
RestApiError(
|
||||||
|
errorCode = -1,
|
||||||
|
errorMsg = failure.message.orEmpty()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isUnit = successType == Unit::class.java
|
val isUnit = successType == Unit::class.java
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ enum class ValidationType(val value: String) {
|
|||||||
SMS("2fa_sms");
|
SMS("2fa_sms");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(value: String): ValidationType = entries.first { it.value == value }
|
fun parse(value: String): ValidationType =
|
||||||
|
entries.firstOrNull { it.value == value }
|
||||||
|
?: throw IllegalArgumentException("Unknown validation type $value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.meloda.fast.network
|
package dev.meloda.fast.network
|
||||||
|
|
||||||
enum class VkErrorCode(val code: Int) {
|
enum class VkErrorCode(val code: Int) {
|
||||||
|
WTF(-1),
|
||||||
UNKNOWN_ERROR(1),
|
UNKNOWN_ERROR(1),
|
||||||
APP_DISABLED(2),
|
APP_DISABLED(2),
|
||||||
UNKNOWN_METHOD(3),
|
UNKNOWN_METHOD(3),
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.slack.eithernet.integration.retrofit.ApiResultCallAdapterFactory
|
|||||||
import com.slack.eithernet.integration.retrofit.ApiResultConverterFactory
|
import com.slack.eithernet.integration.retrofit.ApiResultConverterFactory
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import dev.meloda.fast.common.AppConstants
|
import dev.meloda.fast.common.AppConstants
|
||||||
import dev.meloda.fast.common.model.LogLevel
|
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
import dev.meloda.fast.network.JsonConverter
|
import dev.meloda.fast.network.JsonConverter
|
||||||
import dev.meloda.fast.network.MoshiConverter
|
import dev.meloda.fast.network.MoshiConverter
|
||||||
@@ -57,12 +56,8 @@ val networkModule = module {
|
|||||||
.followSslRedirects(true)
|
.followSslRedirects(true)
|
||||||
.addInterceptor(
|
.addInterceptor(
|
||||||
HttpLoggingInterceptor().apply {
|
HttpLoggingInterceptor().apply {
|
||||||
level = when (AppSettings.Debug.networkLogLevel) {
|
level =
|
||||||
LogLevel.NONE -> HttpLoggingInterceptor.Level.NONE
|
HttpLoggingInterceptor.Level.entries[AppSettings.Debug.networkLogLevel.ordinal]
|
||||||
LogLevel.BASIC -> HttpLoggingInterceptor.Level.BASIC
|
|
||||||
LogLevel.HEADERS -> HttpLoggingInterceptor.Level.HEADERS
|
|
||||||
LogLevel.BODY -> HttpLoggingInterceptor.Level.BODY
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -22,7 +23,9 @@ fun ErrorView(
|
|||||||
onButtonClick: (() -> Unit)? = null,
|
onButtonClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
<string name="message_attachment_story_your_story">Ваша история</string>
|
<string name="message_attachment_story_your_story">Ваша история</string>
|
||||||
<string name="settings_dynamic_colors">Динамические цвета</string>
|
<string name="settings_dynamic_colors">Динамические цвета</string>
|
||||||
<string name="settings_dynamic_colors_description">Цвета для приложения будут извлечены из ваших обоев на главном экране</string>
|
<string name="settings_dynamic_colors_description">Цвета для приложения будут извлечены из ваших обоев на главном экране</string>
|
||||||
|
<string name="settings_appearance_use_system_font_title">Использовать системный шрифт</string>
|
||||||
<string name="settings_application_language">Язык приложения</string>
|
<string name="settings_application_language">Язык приложения</string>
|
||||||
<string name="settings_application_language_value">Текущий: %1$s</string>
|
<string name="settings_application_language_value">Текущий: %1$s</string>
|
||||||
<string name="language_system">Системный</string>
|
<string name="language_system">Системный</string>
|
||||||
@@ -177,6 +178,7 @@
|
|||||||
<string name="settings_general_title">Основное</string>
|
<string name="settings_general_title">Основное</string>
|
||||||
<string name="settings_general_contact_names_title">Использовать имена контактов</string>
|
<string name="settings_general_contact_names_title">Использовать имена контактов</string>
|
||||||
<string name="settings_general_contact_names_summary">Приложение будет использовать доступные имена контактов для пользователей</string>
|
<string name="settings_general_contact_names_summary">Приложение будет использовать доступные имена контактов для пользователей</string>
|
||||||
|
<string name="settings_general_enable_haptic_title">Включить тактильную отдачу</string>
|
||||||
<string name="settings_appearance_title">Внешний вид</string>
|
<string name="settings_appearance_title">Внешний вид</string>
|
||||||
<string name="settings_appearance_multiline_title">Многострочные заголовки и сообщения</string>
|
<string name="settings_appearance_multiline_title">Многострочные заголовки и сообщения</string>
|
||||||
<string name="settings_appearance_multiline_summary">Заголовок чата и текст сообщения смогут занимать несколько строчек</string>
|
<string name="settings_appearance_multiline_summary">Заголовок чата и текст сообщения смогут занимать несколько строчек</string>
|
||||||
@@ -184,9 +186,11 @@
|
|||||||
<string name="settings_features_fast_text_title">Fast текст</string>
|
<string name="settings_features_fast_text_title">Fast текст</string>
|
||||||
<string name="settings_features_long_poll_in_background_title">LongPoll в фоне</string>
|
<string name="settings_features_long_poll_in_background_title">LongPoll в фоне</string>
|
||||||
<string name="settings_features_long_poll_in_background_summary">Ваши сообщения будут обновляться, даже если приложение находится в фоне</string>
|
<string name="settings_features_long_poll_in_background_summary">Ваши сообщения будут обновляться, даже если приложение находится в фоне</string>
|
||||||
|
<string name="settings_experimental_more_animations_summary">Использовать анимации везде, где возможно</string>
|
||||||
<string name="settings_activity_title">Активность</string>
|
<string name="settings_activity_title">Активность</string>
|
||||||
<string name="settings_activity_send_online_title">Быть «в сети»</string>
|
<string name="settings_activity_send_online_title">Быть «в сети»</string>
|
||||||
<string name="settings_activity_send_online_summary">Статус «в сети» будет отправляться каждые 5 минут</string>
|
<string name="settings_activity_send_online_summary">Статус «в сети» будет отправляться каждые 5 минут</string>
|
||||||
|
<string name="settings_experimental_title">Экспериментальные - ОЧЕНЬ нестабильные</string>
|
||||||
<string name="settings_debug_title">Отладка</string>
|
<string name="settings_debug_title">Отладка</string>
|
||||||
<string name="action_disable">Отключить</string>
|
<string name="action_disable">Отключить</string>
|
||||||
<string name="background_long_poll_rationale_text">Приложение не сможет обновлять сообщения в фоне без доступа к уведомлениям</string>
|
<string name="background_long_poll_rationale_text">Приложение не сможет обновлять сообщения в фоне без доступа к уведомлениям</string>
|
||||||
@@ -198,4 +202,14 @@
|
|||||||
<string name="notification_channel_no_category_description">Уведомления без категории</string>
|
<string name="notification_channel_no_category_description">Уведомления без категории</string>
|
||||||
<string name="notification_channel_long_polling_service_name">Сервис обновления сообщений</string>
|
<string name="notification_channel_long_polling_service_name">Сервис обновления сообщений</string>
|
||||||
<string name="notification_channel_long_polling_service_description">Уведомления сервиса обновлений сообщений</string>
|
<string name="notification_channel_long_polling_service_description">Уведомления сервиса обновлений сообщений</string>
|
||||||
|
<string name="settings_general_show_emoji_button_title">Показывать кнопку эмоджи</string>
|
||||||
|
<string name="settings_general_show_emoji_button_summary">Показывать кнопку эмоджи на панели чата</string>
|
||||||
|
<string name="settings_features_show_time_in_action_messages_title">Показывать время в сервисных сообщениях</string>
|
||||||
|
<string name="settings_experimental_use_blur_title">Использовать размытие</string>
|
||||||
|
<string name="settings_experimental_use_blur_summary">Добавлять размытие везде, где возможно.\\nРаботает только с 12 версии Android</string>
|
||||||
|
<string name="settings_experimental_more_animations_title">Больше анимаций</string>
|
||||||
|
<string name="warning_confirmation">Подтверждение</string>
|
||||||
|
<string name="captcha_exit_warning">Вы уверены? Процесс ввода капчи будет отменён</string>
|
||||||
|
<string name="validation_exit_warning">Вы уверены? Процесс ввода кода-подтверждения будет отменён</string>
|
||||||
|
<string name="action_authorize">Авторизоваться</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -204,6 +204,8 @@
|
|||||||
<string name="settings_dynamic_colors">Dynamic colors</string>
|
<string name="settings_dynamic_colors">Dynamic colors</string>
|
||||||
<string name="settings_dynamic_colors_description">The colors for the app will be extracted from your home screen wallpaper</string>
|
<string name="settings_dynamic_colors_description">The colors for the app will be extracted from your home screen wallpaper</string>
|
||||||
|
|
||||||
|
<string name="settings_appearance_use_system_font_title">Use system font</string>
|
||||||
|
|
||||||
<string name="settings_application_language">Application Language</string>
|
<string name="settings_application_language">Application Language</string>
|
||||||
<string name="settings_application_language_value">Current: %1$s</string>
|
<string name="settings_application_language_value">Current: %1$s</string>
|
||||||
|
|
||||||
@@ -235,6 +237,9 @@
|
|||||||
<string name="settings_general_title">General</string>
|
<string name="settings_general_title">General</string>
|
||||||
<string name="settings_general_contact_names_title">Use contact names</string>
|
<string name="settings_general_contact_names_title">Use contact names</string>
|
||||||
<string name="settings_general_contact_names_summary">App will use available contact names for users</string>
|
<string name="settings_general_contact_names_summary">App will use available contact names for users</string>
|
||||||
|
<string name="settings_general_show_emoji_button_title">Show emoji button</string>
|
||||||
|
<string name="settings_general_show_emoji_button_summary">Show emoji button in chat panel</string>
|
||||||
|
<string name="settings_general_enable_haptic_title">Enable haptic</string>
|
||||||
<string name="settings_appearance_title">Appearance</string>
|
<string name="settings_appearance_title">Appearance</string>
|
||||||
<string name="settings_appearance_multiline_title">Multiline titles and messages</string>
|
<string name="settings_appearance_multiline_title">Multiline titles and messages</string>
|
||||||
<string name="settings_appearance_multiline_summary">The title of the conversation and the text of the message can take up multiple lines</string>
|
<string name="settings_appearance_multiline_summary">The title of the conversation and the text of the message can take up multiple lines</string>
|
||||||
@@ -242,9 +247,18 @@
|
|||||||
<string name="settings_features_fast_text_title">Fast text</string>
|
<string name="settings_features_fast_text_title">Fast text</string>
|
||||||
<string name="settings_features_long_poll_in_background_title">LongPoll in background</string>
|
<string name="settings_features_long_poll_in_background_title">LongPoll in background</string>
|
||||||
<string name="settings_features_long_poll_in_background_summary">Your messages will be updating even when app is not on the screen</string>
|
<string name="settings_features_long_poll_in_background_summary">Your messages will be updating even when app is not on the screen</string>
|
||||||
|
<string name="settings_features_show_time_in_action_messages_title">Show time in action messages</string>
|
||||||
|
<string name="settings_experimental_use_blur_title">Use blur</string>
|
||||||
|
<string name="settings_experimental_use_blur_summary">Adds blur wherever possible.\nWorks on android 12 and newer</string>
|
||||||
|
<string name="settings_experimental_more_animations_title">More animations</string>
|
||||||
|
<string name="settings_experimental_more_animations_summary">Use animations wherever possible</string>
|
||||||
|
|
||||||
<string name="settings_activity_title">Activity</string>
|
<string name="settings_activity_title">Activity</string>
|
||||||
<string name="settings_activity_send_online_title">Send online status</string>
|
<string name="settings_activity_send_online_title">Send online status</string>
|
||||||
<string name="settings_activity_send_online_summary">Online status will be sent every five minutes</string>
|
<string name="settings_activity_send_online_summary">Online status will be sent every five minutes</string>
|
||||||
|
|
||||||
|
<string name="settings_experimental_title">Experimental - VERY unstable</string>
|
||||||
|
|
||||||
<string name="settings_debug_title">Debug</string>
|
<string name="settings_debug_title">Debug</string>
|
||||||
<string name="background_long_poll_rationale_text">The app won\'t be able to update messages in the background without access to notifications</string>
|
<string name="background_long_poll_rationale_text">The app won\'t be able to update messages in the background without access to notifications</string>
|
||||||
<string name="action_disable">Disable</string>
|
<string name="action_disable">Disable</string>
|
||||||
|
|||||||
@@ -345,6 +345,13 @@ class LoginViewModelImpl(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is State.Error.TestError -> {
|
||||||
|
val message = stateError.message
|
||||||
|
val error = LoginError.SimpleError(message = message)
|
||||||
|
loginError.update { error }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ sealed class LoginError {
|
|||||||
data object TooManyTries : LoginError()
|
data object TooManyTries : LoginError()
|
||||||
data object WrongValidationCode : LoginError()
|
data object WrongValidationCode : LoginError()
|
||||||
data object WrongValidationCodeFormat : LoginError()
|
data object WrongValidationCodeFormat : LoginError()
|
||||||
|
data class SimpleError(val message: String): LoginError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import dev.meloda.fast.auth.login.LoginViewModel
|
|
||||||
import dev.meloda.fast.auth.login.LoginViewModelImpl
|
|
||||||
import dev.meloda.fast.auth.login.model.CaptchaArguments
|
import dev.meloda.fast.auth.login.model.CaptchaArguments
|
||||||
import dev.meloda.fast.auth.login.model.LoginError
|
import dev.meloda.fast.auth.login.model.LoginError
|
||||||
import dev.meloda.fast.auth.login.model.LoginScreenState
|
import dev.meloda.fast.auth.login.model.LoginScreenState
|
||||||
@@ -441,5 +439,14 @@ fun HandleError(
|
|||||||
confirmText = stringResource(id = UiR.string.ok)
|
confirmText = stringResource(id = UiR.string.ok)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is LoginError.SimpleError -> {
|
||||||
|
MaterialDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = "Error",
|
||||||
|
text = error.message,
|
||||||
|
confirmText = stringResource(id = UiR.string.ok)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -62,8 +62,8 @@ import androidx.compose.ui.unit.LayoutDirection
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import dev.chrisbanes.haze.hazeChild
|
import dev.chrisbanes.haze.hazeSource
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.meloda.fast.chatmaterials.ChatMaterialsViewModel
|
import dev.meloda.fast.chatmaterials.ChatMaterialsViewModel
|
||||||
@@ -137,7 +137,7 @@ fun ChatMaterialsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val titles = listOf("Photos", "Videos", "Audios", "Files", "Links")
|
val titles = listOf("Photos", "Videos", "Audios")//, "Files", "Links")
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val gridState = rememberLazyGridState()
|
val gridState = rememberLazyGridState()
|
||||||
@@ -179,7 +179,7 @@ fun ChatMaterialsScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (currentTheme.enableBlur) {
|
if (currentTheme.enableBlur) {
|
||||||
Modifier.hazeChild(
|
Modifier.hazeEffect(
|
||||||
state = hazeState,
|
state = hazeState,
|
||||||
style = hazeStyle
|
style = hazeStyle
|
||||||
)
|
)
|
||||||
@@ -311,7 +311,7 @@ fun ChatMaterialsScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (currentTheme.enableBlur) {
|
if (currentTheme.enableBlur) {
|
||||||
Modifier.haze(state = hazeState)
|
Modifier.hazeSource(state = hazeState)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
@@ -346,7 +346,7 @@ fun ChatMaterialsScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (currentTheme.enableBlur) {
|
if (currentTheme.enableBlur) {
|
||||||
Modifier.haze(state = hazeState)
|
Modifier.hazeSource(state = hazeState)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
|
|||||||
+162
-31
@@ -1,8 +1,11 @@
|
|||||||
package dev.meloda.fast.conversations
|
package dev.meloda.fast.conversations
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import coil.ImageLoader
|
||||||
|
import coil.request.ImageRequest
|
||||||
import com.conena.nanokt.collections.indexOfFirstOrNull
|
import com.conena.nanokt.collections.indexOfFirstOrNull
|
||||||
import dev.meloda.fast.common.extensions.createTimerFlow
|
import dev.meloda.fast.common.extensions.createTimerFlow
|
||||||
import dev.meloda.fast.common.extensions.findWithIndex
|
import dev.meloda.fast.common.extensions.findWithIndex
|
||||||
@@ -18,6 +21,7 @@ import dev.meloda.fast.data.State
|
|||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
import dev.meloda.fast.datastore.UserSettings
|
import dev.meloda.fast.datastore.UserSettings
|
||||||
import dev.meloda.fast.domain.ConversationsUseCase
|
import dev.meloda.fast.domain.ConversationsUseCase
|
||||||
|
import dev.meloda.fast.domain.LoadConversationsByIdUseCase
|
||||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||||
import dev.meloda.fast.domain.MessagesUseCase
|
import dev.meloda.fast.domain.MessagesUseCase
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
@@ -43,7 +47,6 @@ interface ConversationsViewModel {
|
|||||||
|
|
||||||
val screenState: StateFlow<ConversationsScreenState>
|
val screenState: StateFlow<ConversationsScreenState>
|
||||||
val baseError: StateFlow<BaseError?>
|
val baseError: StateFlow<BaseError?>
|
||||||
val imagesToPreload: StateFlow<List<String>>
|
|
||||||
val currentOffset: StateFlow<Int>
|
val currentOffset: StateFlow<Int>
|
||||||
val canPaginate: StateFlow<Boolean>
|
val canPaginate: StateFlow<Boolean>
|
||||||
val scrollToTop: StateFlow<Boolean>
|
val scrollToTop: StateFlow<Boolean>
|
||||||
@@ -78,16 +81,23 @@ class ConversationsViewModelImpl(
|
|||||||
private val conversationsUseCase: ConversationsUseCase,
|
private val conversationsUseCase: ConversationsUseCase,
|
||||||
private val messagesUseCase: MessagesUseCase,
|
private val messagesUseCase: MessagesUseCase,
|
||||||
private val resources: Resources,
|
private val resources: Resources,
|
||||||
private val userSettings: UserSettings
|
private val userSettings: UserSettings,
|
||||||
|
private val imageLoader: ImageLoader,
|
||||||
|
private val applicationContext: Context,
|
||||||
|
private val loadConversationsByIdUseCase: LoadConversationsByIdUseCase
|
||||||
) : ConversationsViewModel, ViewModel() {
|
) : ConversationsViewModel, ViewModel() {
|
||||||
|
|
||||||
override val screenState = MutableStateFlow(ConversationsScreenState.EMPTY)
|
override val screenState = MutableStateFlow(ConversationsScreenState.EMPTY)
|
||||||
override val baseError = MutableStateFlow<BaseError?>(null)
|
override val baseError = MutableStateFlow<BaseError?>(null)
|
||||||
override val imagesToPreload = MutableStateFlow<List<String>>(emptyList())
|
|
||||||
override val currentOffset = MutableStateFlow(0)
|
override val currentOffset = MutableStateFlow(0)
|
||||||
override val canPaginate = MutableStateFlow(false)
|
override val canPaginate = MutableStateFlow(false)
|
||||||
override val scrollToTop = MutableStateFlow(false)
|
override val scrollToTop = MutableStateFlow(false)
|
||||||
|
|
||||||
|
// TODO: 22-Dec-24, Danil Nikolaev: rewrite
|
||||||
|
private val useContactNames = {
|
||||||
|
userSettings.useContactNames.value
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPaginationConditionsMet() {
|
override fun onPaginationConditionsMet() {
|
||||||
currentOffset.update { screenState.value.conversations.size }
|
currentOffset.update { screenState.value.conversations.size }
|
||||||
loadConversations()
|
loadConversations()
|
||||||
@@ -124,6 +134,7 @@ class ConversationsViewModelImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRefresh() {
|
override fun onRefresh() {
|
||||||
|
baseError.setValue { null }
|
||||||
loadConversations(offset = 0)
|
loadConversations(offset = 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,17 +274,7 @@ class ConversationsViewModelImpl(
|
|||||||
conversationsUseCase.getConversations(count = LOAD_COUNT, offset = offset)
|
conversationsUseCase.getConversations(count = LOAD_COUNT, offset = offset)
|
||||||
.listenValue(viewModelScope) { state ->
|
.listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = ::handleError,
|
||||||
if (error is State.Error.ApiError) {
|
|
||||||
when (error.errorCode) {
|
|
||||||
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
|
|
||||||
baseError.setValue { BaseError.SessionExpired }
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val itemsCountSufficient = response.size == LOAD_COUNT
|
val itemsCountSufficient = response.size == LOAD_COUNT
|
||||||
canPaginate.setValue { itemsCountSufficient }
|
canPaginate.setValue { itemsCountSufficient }
|
||||||
@@ -281,9 +282,17 @@ class ConversationsViewModelImpl(
|
|||||||
val paginationExhausted = !itemsCountSufficient &&
|
val paginationExhausted = !itemsCountSufficient &&
|
||||||
screenState.value.conversations.isNotEmpty()
|
screenState.value.conversations.isNotEmpty()
|
||||||
|
|
||||||
imagesToPreload.setValue {
|
val imagesToPreload =
|
||||||
response.mapNotNull { it.extractAvatar().extractUrl() }
|
response.mapNotNull { it.extractAvatar().extractUrl() }
|
||||||
|
|
||||||
|
imagesToPreload.forEach { url ->
|
||||||
|
imageLoader.enqueue(
|
||||||
|
ImageRequest.Builder(applicationContext)
|
||||||
|
.data(url)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
conversationsUseCase.storeConversations(response)
|
conversationsUseCase.storeConversations(response)
|
||||||
|
|
||||||
val loadedConversations = response.map {
|
val loadedConversations = response.map {
|
||||||
@@ -321,6 +330,40 @@ class ConversationsViewModelImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleError(error: State.Error) {
|
||||||
|
when (error) {
|
||||||
|
is State.Error.ApiError -> {
|
||||||
|
when (error.errorCode) {
|
||||||
|
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
|
||||||
|
baseError.setValue { BaseError.SessionExpired }
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
baseError.setValue {
|
||||||
|
BaseError.SimpleError(message = error.errorMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State.Error.ConnectionError -> {
|
||||||
|
baseError.setValue {
|
||||||
|
BaseError.SimpleError(message = "Connection error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State.Error.InternalError -> {
|
||||||
|
baseError.setValue {
|
||||||
|
BaseError.SimpleError(message = "Internal error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State.Error.UnknownError -> {
|
||||||
|
baseError.setValue {
|
||||||
|
BaseError.SimpleError(message = "Unknown error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun deleteConversation(peerId: Int) {
|
private fun deleteConversation(peerId: Int) {
|
||||||
conversationsUseCase.delete(peerId).listenValue(viewModelScope) { state ->
|
conversationsUseCase.delete(peerId).listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
@@ -337,7 +380,12 @@ class ConversationsViewModelImpl(
|
|||||||
conversations.update { newConversations }
|
conversations.update { newConversations }
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,13 +421,40 @@ class ConversationsViewModelImpl(
|
|||||||
|
|
||||||
private fun handleNewMessage(event: LongPollEvent.VkMessageNewEvent) {
|
private fun handleNewMessage(event: LongPollEvent.VkMessageNewEvent) {
|
||||||
val message = event.message
|
val message = event.message
|
||||||
|
|
||||||
val newConversations = conversations.value.toMutableList()
|
val newConversations = conversations.value.toMutableList()
|
||||||
val conversationIndex =
|
val conversationIndex =
|
||||||
newConversations.indexOfFirstOrNull { it.id == message.peerId }
|
newConversations.indexOfFirstOrNull { it.id == message.peerId }
|
||||||
|
|
||||||
if (conversationIndex == null) { // диалога нет в списке
|
if (conversationIndex == null) {
|
||||||
// pizdets
|
loadConversationsByIdUseCase(peerIds = listOf(message.peerId))
|
||||||
// TODO: 04/07/2024, Danil Nikolaev: load conversation and store info
|
.listenValue(viewModelScope) { state ->
|
||||||
|
state.processState(
|
||||||
|
error = { error ->
|
||||||
|
|
||||||
|
},
|
||||||
|
success = { response ->
|
||||||
|
val conversation = (response.firstOrNull() ?: return@listenValue)
|
||||||
|
.copy(lastMessage = message)
|
||||||
|
|
||||||
|
// TODO: 22-Dec-24, Danil Nikolaev: handle interactions and pinned state
|
||||||
|
|
||||||
|
newConversations.add(pinnedConversationsCount.value, conversation)
|
||||||
|
conversations.update { newConversations }
|
||||||
|
|
||||||
|
screenState.setValue { old ->
|
||||||
|
old.copy(
|
||||||
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val conversation = newConversations[conversationIndex]
|
val conversation = newConversations[conversationIndex]
|
||||||
var newConversation = conversation.copy(
|
var newConversation = conversation.copy(
|
||||||
@@ -420,7 +495,12 @@ class ConversationsViewModelImpl(
|
|||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,7 +524,12 @@ class ConversationsViewModelImpl(
|
|||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -454,8 +539,11 @@ class ConversationsViewModelImpl(
|
|||||||
val newConversations = conversations.value.toMutableList()
|
val newConversations = conversations.value.toMutableList()
|
||||||
|
|
||||||
val conversationIndex =
|
val conversationIndex =
|
||||||
newConversations.indexOfFirstOrNull { it.id == event.peerId } ?: return
|
newConversations.indexOfFirstOrNull { it.id == event.peerId }
|
||||||
|
|
||||||
|
if (conversationIndex == null) { // диалога нет в списке
|
||||||
|
// pizdets
|
||||||
|
} else {
|
||||||
newConversations[conversationIndex] =
|
newConversations[conversationIndex] =
|
||||||
newConversations[conversationIndex].copy(
|
newConversations[conversationIndex].copy(
|
||||||
inRead = event.messageId,
|
inRead = event.messageId,
|
||||||
@@ -466,17 +554,26 @@ class ConversationsViewModelImpl(
|
|||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReadOutgoingMessage(event: LongPollEvent.VkMessageReadOutgoingEvent) {
|
private fun handleReadOutgoingMessage(event: LongPollEvent.VkMessageReadOutgoingEvent) {
|
||||||
val newConversations = conversations.value.toMutableList()
|
val newConversations = conversations.value.toMutableList()
|
||||||
|
|
||||||
val conversationIndex =
|
val conversationIndex =
|
||||||
newConversations.indexOfFirstOrNull { it.id == event.peerId } ?: return
|
newConversations.indexOfFirstOrNull { it.id == event.peerId }
|
||||||
|
|
||||||
|
if (conversationIndex == null) { // диалога нет в списке
|
||||||
|
// pizdets
|
||||||
|
} else {
|
||||||
newConversations[conversationIndex] =
|
newConversations[conversationIndex] =
|
||||||
newConversations[conversationIndex].copy(
|
newConversations[conversationIndex].copy(
|
||||||
outRead = event.messageId,
|
outRead = event.messageId,
|
||||||
@@ -486,9 +583,15 @@ class ConversationsViewModelImpl(
|
|||||||
conversations.update { newConversations }
|
conversations.update { newConversations }
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handlePinStateChanged(event: LongPollEvent.VkConversationPinStateChangedEvent) {
|
private fun handlePinStateChanged(event: LongPollEvent.VkConversationPinStateChangedEvent) {
|
||||||
@@ -496,8 +599,11 @@ class ConversationsViewModelImpl(
|
|||||||
val newConversations = conversations.value.toMutableList()
|
val newConversations = conversations.value.toMutableList()
|
||||||
|
|
||||||
val conversationIndex =
|
val conversationIndex =
|
||||||
newConversations.indexOfFirstOrNull { it.id == event.peerId } ?: return
|
newConversations.indexOfFirstOrNull { it.id == event.peerId }
|
||||||
|
|
||||||
|
if (conversationIndex == null) { // диалога нет в списке
|
||||||
|
// pizdets
|
||||||
|
} else {
|
||||||
val pin = event.majorId > 0
|
val pin = event.majorId > 0
|
||||||
|
|
||||||
val conversation = newConversations[conversationIndex].copy(majorId = event.majorId)
|
val conversation = newConversations[conversationIndex].copy(majorId = event.majorId)
|
||||||
@@ -523,7 +629,13 @@ class ConversationsViewModelImpl(
|
|||||||
conversations.update { newConversations }
|
conversations.update { newConversations }
|
||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(conversations = newConversations.map { it.asPresentation(resources) })
|
old.copy(conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,8 +655,11 @@ class ConversationsViewModelImpl(
|
|||||||
|
|
||||||
val newConversations = conversations.value.toMutableList()
|
val newConversations = conversations.value.toMutableList()
|
||||||
val conversationAndIndex =
|
val conversationAndIndex =
|
||||||
newConversations.findWithIndex { it.id == peerId } ?: return
|
newConversations.findWithIndex { it.id == peerId }
|
||||||
|
|
||||||
|
if (conversationAndIndex == null) { // диалога нет в списке
|
||||||
|
// pizdets
|
||||||
|
} else {
|
||||||
newConversations[conversationAndIndex.first] =
|
newConversations[conversationAndIndex.first] =
|
||||||
conversationAndIndex.second.copy(
|
conversationAndIndex.second.copy(
|
||||||
interactionType = interactionType.value,
|
interactionType = interactionType.value,
|
||||||
@@ -555,7 +670,12 @@ class ConversationsViewModelImpl(
|
|||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,6 +703,7 @@ class ConversationsViewModelImpl(
|
|||||||
stopInteraction(peerId, newInteractionJob)
|
stopInteraction(peerId, newInteractionJob)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun stopInteraction(peerId: Int, interactionJob: InteractionJob) {
|
private fun stopInteraction(peerId: Int, interactionJob: InteractionJob) {
|
||||||
interactionsTimers[peerId] ?: return
|
interactionsTimers[peerId] ?: return
|
||||||
@@ -600,7 +721,12 @@ class ConversationsViewModelImpl(
|
|||||||
conversations.update { newConversations }
|
conversations.update { newConversations }
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +755,12 @@ class ConversationsViewModelImpl(
|
|||||||
conversations.update { newConversations }
|
conversations.update { newConversations }
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
conversations = newConversations.map { it.asPresentation(resources) }
|
conversations = newConversations.map {
|
||||||
|
it.asPresentation(
|
||||||
|
resources = resources,
|
||||||
|
useContactName = useContactNames()
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-17
@@ -54,7 +54,6 @@ import androidx.compose.runtime.snapshotFlow
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -65,8 +64,6 @@ import androidx.compose.ui.unit.LayoutDirection
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.HapticFeedbackConstantsCompat
|
import androidx.core.view.HapticFeedbackConstantsCompat
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import coil.imageLoader
|
|
||||||
import coil.request.ImageRequest
|
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
import dev.chrisbanes.haze.hazeChild
|
import dev.chrisbanes.haze.hazeChild
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
@@ -96,24 +93,11 @@ fun ConversationsRoute(
|
|||||||
onConversationPhotoClicked: (url: String) -> Unit,
|
onConversationPhotoClicked: (url: String) -> Unit,
|
||||||
viewModel: ConversationsViewModel
|
viewModel: ConversationsViewModel
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||||
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||||
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
||||||
val isNeedToScrollToTop by viewModel.scrollToTop.collectAsStateWithLifecycle()
|
val isNeedToScrollToTop by viewModel.scrollToTop.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val imagesToPreload by viewModel.imagesToPreload.collectAsStateWithLifecycle()
|
|
||||||
LaunchedEffect(imagesToPreload) {
|
|
||||||
imagesToPreload.forEach { url ->
|
|
||||||
context.imageLoader.enqueue(
|
|
||||||
ImageRequest.Builder(context)
|
|
||||||
.data(url)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConversationsScreen(
|
ConversationsScreen(
|
||||||
screenState = screenState,
|
screenState = screenState,
|
||||||
baseError = baseError,
|
baseError = baseError,
|
||||||
@@ -355,7 +339,9 @@ fun ConversationsScreen(
|
|||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
when {
|
when {
|
||||||
baseError is BaseError.SessionExpired -> {
|
baseError != null -> {
|
||||||
|
when (baseError) {
|
||||||
|
is BaseError.SessionExpired -> {
|
||||||
ErrorView(
|
ErrorView(
|
||||||
text = "Session expired",
|
text = "Session expired",
|
||||||
buttonText = "Log out",
|
buttonText = "Log out",
|
||||||
@@ -363,6 +349,16 @@ fun ConversationsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is BaseError.SimpleError -> {
|
||||||
|
ErrorView(
|
||||||
|
text = baseError.message,
|
||||||
|
buttonText = "Try again",
|
||||||
|
onButtonClick = onRefresh
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
screenState.isLoading && screenState.conversations.isEmpty() -> FullScreenLoader()
|
screenState.isLoading && screenState.conversations.isEmpty() -> FullScreenLoader()
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ import dev.meloda.fast.ui.R as UiR
|
|||||||
|
|
||||||
fun VkConversation.asPresentation(
|
fun VkConversation.asPresentation(
|
||||||
resources: Resources,
|
resources: Resources,
|
||||||
useContactName: Boolean = false
|
useContactName: Boolean
|
||||||
): UiConversation = UiConversation(
|
): UiConversation = UiConversation(
|
||||||
id = id,
|
id = id,
|
||||||
lastMessageId = lastMessageId,
|
lastMessageId = lastMessageId,
|
||||||
|
|||||||
@@ -278,14 +278,14 @@ class SettingsViewModelImpl(
|
|||||||
)
|
)
|
||||||
val generalShowEmojiButton = SettingsItem.Switch(
|
val generalShowEmojiButton = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_SHOW_EMOJI_BUTTON,
|
key = SettingsKeys.KEY_SHOW_EMOJI_BUTTON,
|
||||||
title = UiText.Simple("Show emoji button"),
|
title = UiText.Resource(UiR.string.settings_general_show_emoji_button_title),
|
||||||
text = UiText.Simple("Show emoji button in chat panel"),
|
text = UiText.Resource(UiR.string.settings_general_show_emoji_button_summary),
|
||||||
defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON
|
defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON
|
||||||
)
|
)
|
||||||
val generalEnableHaptic = SettingsItem.Switch(
|
val generalEnableHaptic = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_ENABLE_HAPTIC,
|
key = SettingsKeys.KEY_ENABLE_HAPTIC,
|
||||||
defaultValue = SettingsKeys.DEFAULT_ENABLE_HAPTIC,
|
defaultValue = SettingsKeys.DEFAULT_ENABLE_HAPTIC,
|
||||||
title = UiText.Simple("Enable haptic")
|
title = UiText.Resource(UiR.string.settings_general_enable_haptic_title)
|
||||||
)
|
)
|
||||||
|
|
||||||
val appearanceTitle = SettingsItem.Title(
|
val appearanceTitle = SettingsItem.Title(
|
||||||
@@ -342,7 +342,7 @@ class SettingsViewModelImpl(
|
|||||||
val appearanceUseSystemFont = SettingsItem.Switch(
|
val appearanceUseSystemFont = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_USE_SYSTEM_FONT,
|
key = SettingsKeys.KEY_USE_SYSTEM_FONT,
|
||||||
defaultValue = SettingsKeys.DEFAULT_USE_SYSTEM_FONT,
|
defaultValue = SettingsKeys.DEFAULT_USE_SYSTEM_FONT,
|
||||||
title = UiText.Simple("Use system font")
|
title = UiText.Resource(UiR.string.settings_appearance_use_system_font_title)
|
||||||
)
|
)
|
||||||
val appearanceLanguage = SettingsItem.TitleText(
|
val appearanceLanguage = SettingsItem.TitleText(
|
||||||
key = SettingsKeys.KEY_APPEARANCE_LANGUAGE,
|
key = SettingsKeys.KEY_APPEARANCE_LANGUAGE,
|
||||||
@@ -379,7 +379,7 @@ class SettingsViewModelImpl(
|
|||||||
|
|
||||||
val experimentalTitle = SettingsItem.Title(
|
val experimentalTitle = SettingsItem.Title(
|
||||||
key = "experimental",
|
key = "experimental",
|
||||||
title = UiText.Simple("Experimental - VERY unstable")
|
title = UiText.Resource(UiR.string.settings_experimental_title)
|
||||||
)
|
)
|
||||||
val experimentalLongPollBackground = SettingsItem.Switch(
|
val experimentalLongPollBackground = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_LONG_POLL_IN_BACKGROUND,
|
key = SettingsKeys.KEY_LONG_POLL_IN_BACKGROUND,
|
||||||
@@ -390,19 +390,20 @@ class SettingsViewModelImpl(
|
|||||||
val experimentalShowTimeInActionMessages = SettingsItem.Switch(
|
val experimentalShowTimeInActionMessages = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_SHOW_TIME_IN_ACTION_MESSAGES,
|
key = SettingsKeys.KEY_SHOW_TIME_IN_ACTION_MESSAGES,
|
||||||
defaultValue = SettingsKeys.DEFAULT_SHOW_TIME_IN_ACTION_MESSAGES,
|
defaultValue = SettingsKeys.DEFAULT_SHOW_TIME_IN_ACTION_MESSAGES,
|
||||||
title = UiText.Simple("Show time in action messages")
|
title = UiText.Resource(UiR.string.settings_features_show_time_in_action_messages_title)
|
||||||
)
|
)
|
||||||
val experimentalUseBlur = SettingsItem.Switch(
|
val experimentalUseBlur = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_USE_BLUR,
|
key = SettingsKeys.KEY_USE_BLUR,
|
||||||
defaultValue = SettingsKeys.DEFAULT_USE_BLUR,
|
defaultValue = SettingsKeys.DEFAULT_USE_BLUR,
|
||||||
title = UiText.Simple("Use blur"),
|
title = UiText.Resource(UiR.string.settings_experimental_use_blur_title),
|
||||||
text = UiText.Simple("Adds blur wherever possible\nWorks on android 12 and newer"),
|
text = UiText.Resource(UiR.string.settings_experimental_use_blur_summary),
|
||||||
|
isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||||
)
|
)
|
||||||
val enableAnimations = SettingsItem.Switch(
|
val enableAnimations = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_MORE_ANIMATIONS,
|
key = SettingsKeys.KEY_MORE_ANIMATIONS,
|
||||||
defaultValue = SettingsKeys.DEFAULT_MORE_ANIMATIONS,
|
defaultValue = SettingsKeys.DEFAULT_MORE_ANIMATIONS,
|
||||||
title = UiText.Simple("More animations"),
|
title = UiText.Resource(UiR.string.settings_experimental_more_animations_title),
|
||||||
text = UiText.Simple("Use animations wherever possible")
|
text = UiText.Resource(UiR.string.settings_experimental_more_animations_summary)
|
||||||
)
|
)
|
||||||
|
|
||||||
val debugTitle = SettingsItem.Title(
|
val debugTitle = SettingsItem.Title(
|
||||||
|
|||||||
+14
-14
@@ -2,25 +2,25 @@
|
|||||||
minSdk = "23"
|
minSdk = "23"
|
||||||
targetSdk = "35"
|
targetSdk = "35"
|
||||||
compileSdk = "35"
|
compileSdk = "35"
|
||||||
versionCode = "8"
|
versionCode = "9"
|
||||||
versionName = "0.1.5"
|
versionName = "0.1.6"
|
||||||
|
|
||||||
agp = "8.7.3"
|
agp = "8.9.0"
|
||||||
converterMoshi = "2.11.0"
|
converterMoshi = "2.11.0"
|
||||||
eithernet = "2.0.0"
|
eithernet = "2.0.0"
|
||||||
haze = "1.1.1"
|
haze = "1.5.1"
|
||||||
kotlin = "2.1.0"
|
kotlin = "2.1.10"
|
||||||
ksp = "2.1.0-1.0.29"
|
ksp = "2.1.10-1.0.31"
|
||||||
|
|
||||||
compose-bom = "2024.12.01"
|
compose-bom = "2025.03.00"
|
||||||
koin = "4.0.0"
|
koin = "4.0.2"
|
||||||
|
|
||||||
accompanist = "0.37.0"
|
accompanist = "0.37.2"
|
||||||
coil = "2.7.0"
|
coil = "2.7.0"
|
||||||
coroutines = "1.9.0"
|
coroutines = "1.10.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
chucker = "4.1.0"
|
chucker = "4.1.0"
|
||||||
guava = "33.3.1-jre"
|
guava = "33.4.5-jre"
|
||||||
lifecycle = "2.8.7"
|
lifecycle = "2.8.7"
|
||||||
core-ktx = "1.15.0"
|
core-ktx = "1.15.0"
|
||||||
material = "1.12.0"
|
material = "1.12.0"
|
||||||
@@ -33,10 +33,10 @@ nanokt = "1.2.0"
|
|||||||
junitVersion = "1.2.1"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.6.1"
|
espressoCore = "3.6.1"
|
||||||
appcompat = "1.7.0"
|
appcompat = "1.7.0"
|
||||||
androidx-navigation = "2.8.5"
|
androidx-navigation = "2.8.9"
|
||||||
serialization = "1.7.3"
|
serialization = "1.8.0"
|
||||||
rebugger = "1.0.0-rc03"
|
rebugger = "1.0.0-rc03"
|
||||||
moduleGraph = "2.7.1"
|
moduleGraph = "2.8.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
#Mon Oct 28 18:41:43 MSK 2024
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,13 +82,12 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -133,22 +134,29 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
# double quotes to make sure that they get re-expanded; and
|
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
@@ -205,6 +217,12 @@ set -- \
|
|||||||
org.gradle.wrapper.GradleWrapperMain \
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
|||||||
Vendored
+21
-16
@@ -13,8 +13,10 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +27,8 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
Reference in New Issue
Block a user