Кешированные ответы от сервера теперь находятся в специальном файле, а не в настройках.

Возвращён просмотр подробностей о паре.
This commit is contained in:
2025-03-20 04:40:57 +04:00
parent 15894b290a
commit 96f84b9f54
37 changed files with 396 additions and 90 deletions

6
.idea/kotlinc.xml generated
View File

@@ -7,10 +7,10 @@
<option name="jvmTarget" value="1.8" /> <option name="jvmTarget" value="1.8" />
</component> </component>
<component name="KotlinCommonCompilerArguments"> <component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.0" /> <option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.0" /> <option name="languageVersion" value="2.1" />
</component> </component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.10" /> <option name="version" value="2.1.10" />
</component> </component>
</project> </project>

View File

@@ -46,8 +46,8 @@ android {
applicationId = "ru.n08i40k.polytechnic.next" applicationId = "ru.n08i40k.polytechnic.next"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 27 versionCode = 28
versionName = "3.1.0" versionName = "3.1.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -158,7 +158,7 @@ dependencies {
protobuf { protobuf {
protoc { protoc {
artifact = "com.google.protobuf:protoc:4.29.3" artifact = "com.google.protobuf:protoc:21.0-rc-1"
} }
plugins { plugins {

View File

@@ -18,7 +18,8 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.app.AppContainer import ru.n08i40k.polytechnic.next.app.AppContainer
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.proto.settings_v0
import ru.n08i40k.polytechnic.next.utils.Observable import ru.n08i40k.polytechnic.next.utils.Observable
import ru.n08i40k.polytechnic.next.worker.UpdateFCMTokenWorker import ru.n08i40k.polytechnic.next.worker.UpdateFCMTokenWorker
import ru.n08i40k.polytechnic.next.worker.UpdateLinkWorker import ru.n08i40k.polytechnic.next.worker.UpdateLinkWorker
@@ -97,8 +98,35 @@ class Application : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
runBlocking { fixupSettings() }
VKID.init(this) VKID.init(this)
setupFirebase() setupFirebase()
} }
private suspend fun fixupSettings() {
val accessToken = this.settings_v0.data.map { it.accessToken }.first()
if (accessToken.isEmpty())
return
val userId = this.settings_v0.data.map { it.userId }.first()
val group = this.settings_v0.data.map { it.group }.first()
val version = this.settings_v0.data.map { it.version }.first()
val fcmToken = this.settings_v0.data.map { it.fcmToken }.first()
this.settings.updateData {
it
.toBuilder()
.setUserId(userId)
.setAccessToken(accessToken)
.setGroup(group)
.setVersion(version)
.setFcmToken(fcmToken)
.build()
}
this.settings_v0.updateData { it.toBuilder().clear().build() }
}
} }

View File

@@ -22,7 +22,7 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.n08i40k.polytechnic.next.app.NotificationChannels import ru.n08i40k.polytechnic.next.app.NotificationChannels
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.PolytechnicApp import ru.n08i40k.polytechnic.next.ui.PolytechnicApp
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
import ru.n08i40k.polytechnic.next.utils.app import ru.n08i40k.polytechnic.next.utils.app

View File

@@ -5,6 +5,7 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.utils.dayMinutes
import ru.n08i40k.polytechnic.next.utils.limit import ru.n08i40k.polytechnic.next.utils.limit
@Parcelize @Parcelize
@@ -17,6 +18,8 @@ data class Lesson(
val group: String? = null, val group: String? = null,
val subGroups: List<SubGroup> val subGroups: List<SubGroup>
) : Parcelable { ) : Parcelable {
val duration: Int get() = time.end.dayMinutes - time.start.dayMinutes
fun getShortName(context: Context): String { fun getShortName(context: Context): String {
val name = val name =
if (type == LessonType.BREAK) if (type == LessonType.BREAK)

View File

@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.app.AppContainer import ru.n08i40k.polytechnic.next.app.AppContainer
import ru.n08i40k.polytechnic.next.network.RequestBase import ru.n08i40k.polytechnic.next.network.RequestBase
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
open class AuthorizedRequest( open class AuthorizedRequest(
val appContainer: AppContainer, val appContainer: AppContainer,

View File

@@ -0,0 +1,29 @@
package ru.n08i40k.polytechnic.next.proto
import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataStore
import androidx.datastore.core.Serializer
import androidx.datastore.dataStore
import com.google.protobuf.InvalidProtocolBufferException
import ru.n08i40k.polytechnic.next.Cache
import java.io.InputStream
import java.io.OutputStream
object CacheSerializer : Serializer<Cache> {
override val defaultValue: Cache = Cache.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Cache =
try {
Cache.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
override suspend fun writeTo(t: Cache, output: OutputStream) = t.writeTo(output)
}
val Context.cache: DataStore<Cache> by dataStore(
fileName = "cache.pb",
serializer = CacheSerializer
)

View File

@@ -1,4 +1,4 @@
package ru.n08i40k.polytechnic.next.settings package ru.n08i40k.polytechnic.next.proto
import android.content.Context import android.content.Context
import androidx.datastore.core.CorruptionException import androidx.datastore.core.CorruptionException
@@ -23,7 +23,7 @@ object SettingsSerializer : Serializer<Settings> {
override suspend fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output) override suspend fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output)
} }
val Context.settings: DataStore<Settings> by dataStore( val Context.settings_v0: DataStore<Settings> by dataStore(
fileName = "settings.pb", fileName = "settings.pb",
serializer = SettingsSerializer serializer = SettingsSerializer
) )

View File

@@ -0,0 +1,29 @@
package ru.n08i40k.polytechnic.next.proto
import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataStore
import androidx.datastore.core.Serializer
import androidx.datastore.dataStore
import com.google.protobuf.InvalidProtocolBufferException
import ru.n08i40k.polytechnic.next.SettingsV2
import java.io.InputStream
import java.io.OutputStream
object SettingsV2Serializer : Serializer<SettingsV2> {
override val defaultValue: SettingsV2 = SettingsV2.getDefaultInstance()
override suspend fun readFrom(input: InputStream): SettingsV2 =
try {
SettingsV2.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
override suspend fun writeTo(t: SettingsV2, output: OutputStream) = t.writeTo(output)
}
val Context.settings: DataStore<SettingsV2> by dataStore(
fileName = "settings-v2.pb",
serializer = SettingsV2Serializer
)

View File

@@ -1,12 +1,12 @@
package ru.n08i40k.polytechnic.next.repository.cache package ru.n08i40k.polytechnic.next.repository.cache
import ru.n08i40k.polytechnic.next.CachedResponse import ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.CacheResponse
interface NetworkCacheRepository { interface NetworkCacheRepository {
suspend fun put(url: String, data: String) suspend fun put(url: String, data: String)
suspend fun get(url: String): CachedResponse? suspend fun get(url: String): CacheResponse?
suspend fun clear() suspend fun clear()
@@ -14,7 +14,7 @@ interface NetworkCacheRepository {
suspend fun setHash(hash: String) suspend fun setHash(hash: String)
suspend fun getUpdateDates(): UpdateDates suspend fun getUpdateDates(): CacheDate
suspend fun setUpdateDates(cache: Long, schedule: Long) suspend fun setUpdateDates(cache: Long, schedule: Long)
} }

View File

@@ -5,17 +5,18 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import ru.n08i40k.polytechnic.next.CachedResponse import ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.CacheResponse
import ru.n08i40k.polytechnic.next.app.AppContainer import ru.n08i40k.polytechnic.next.app.AppContainer
import ru.n08i40k.polytechnic.next.proto.cache
import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository
import ru.n08i40k.polytechnic.next.settings.settings
import javax.inject.Inject import javax.inject.Inject
class LocalNetworkCacheRepository class LocalNetworkCacheRepository
@Inject constructor(private val appContainer: AppContainer) : NetworkCacheRepository { @Inject constructor(private val appContainer: AppContainer) : NetworkCacheRepository {
private val cacheMap: MutableMap<String, CachedResponse> = mutableMapOf() private val cacheMap: MutableMap<String, CacheResponse> = mutableMapOf()
private var updateDates: UpdateDates = UpdateDates.newBuilder().build() private var cacheDate: CacheDate = CacheDate.newBuilder().build()
private var hash: String? = null private var hash: String? = null
private val context get() = appContainer.context private val context get() = appContainer.context
@@ -26,14 +27,14 @@ class LocalNetworkCacheRepository
runBlocking { runBlocking {
cacheMap.putAll( cacheMap.putAll(
context context
.settings .cache
.data .data
.map { settings -> settings.cacheStorageMap }.first() .map { it.storageMap }.first()
) )
} }
} }
override suspend fun get(url: String): CachedResponse? { override suspend fun get(url: String): CacheResponse? {
// Если кешированного ответа нет, то возвращаем null // Если кешированного ответа нет, то возвращаем null
// Если хеши не совпадают и локальный хеш присутствует, то возвращаем null // Если хеши не совпадают и локальный хеш присутствует, то возвращаем null
@@ -49,7 +50,7 @@ class LocalNetworkCacheRepository
if (hash == null) if (hash == null)
throw IllegalStateException("Не установлен хеш!") throw IllegalStateException("Не установлен хеш!")
cacheMap[url] = CachedResponse cacheMap[url] = CacheResponse
.newBuilder() .newBuilder()
.setHash(this.hash) .setHash(this.hash)
.setData(data) .setData(data)
@@ -83,21 +84,21 @@ class LocalNetworkCacheRepository
} }
} }
override suspend fun getUpdateDates(): UpdateDates { override suspend fun getUpdateDates(): CacheDate {
return this.updateDates return this.cacheDate
} }
override suspend fun setUpdateDates(cache: Long, schedule: Long) { override suspend fun setUpdateDates(cache: Long, schedule: Long) {
updateDates = UpdateDates cacheDate = CacheDate
.newBuilder() .newBuilder()
.setCache(cache) .setCache(cache)
.setSchedule(schedule).build() .setSchedule(schedule).build()
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
context.settings.updateData { context.cache.updateData {
it it
.toBuilder() .toBuilder()
.setUpdateDates(updateDates) .setDate(cacheDate)
.build() .build()
} }
} }
@@ -106,10 +107,10 @@ class LocalNetworkCacheRepository
private suspend fun save() { private suspend fun save() {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
context.settings.updateData { context.cache.updateData {
it it
.toBuilder() .toBuilder()
.putAllCacheStorage(cacheMap) .putAllStorage(cacheMap)
.build() .build()
} }
} }

View File

@@ -1,11 +1,11 @@
package ru.n08i40k.polytechnic.next.repository.cache.impl package ru.n08i40k.polytechnic.next.repository.cache.impl
import ru.n08i40k.polytechnic.next.CachedResponse import ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.CacheResponse
import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository
class MockNetworkCacheRepository : NetworkCacheRepository { class MockNetworkCacheRepository : NetworkCacheRepository {
override suspend fun get(url: String): CachedResponse? { override suspend fun get(url: String): CacheResponse? {
return null return null
} }
@@ -19,8 +19,8 @@ class MockNetworkCacheRepository : NetworkCacheRepository {
override suspend fun setHash(hash: String) {} override suspend fun setHash(hash: String) {}
override suspend fun getUpdateDates(): UpdateDates { override suspend fun getUpdateDates(): CacheDate {
return UpdateDates.newBuilder().build() return CacheDate.newBuilder().build()
} }
override suspend fun setUpdateDates(cache: Long, schedule: Long) {} override suspend fun setUpdateDates(cache: Long, schedule: Long) {}

View File

@@ -7,8 +7,9 @@ import ru.n08i40k.polytechnic.next.model.Profile
import ru.n08i40k.polytechnic.next.network.request.fcm.FcmSetToken import ru.n08i40k.polytechnic.next.network.request.fcm.FcmSetToken
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileMe import ru.n08i40k.polytechnic.next.network.request.profile.ProfileMe
import ru.n08i40k.polytechnic.next.network.tryFuture import ru.n08i40k.polytechnic.next.network.tryFuture
import ru.n08i40k.polytechnic.next.proto.cache
import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.repository.profile.ProfileRepository import ru.n08i40k.polytechnic.next.repository.profile.ProfileRepository
import ru.n08i40k.polytechnic.next.settings.settings
import ru.n08i40k.polytechnic.next.utils.MyResult import ru.n08i40k.polytechnic.next.utils.MyResult
import ru.n08i40k.polytechnic.next.utils.app import ru.n08i40k.polytechnic.next.utils.app
@@ -40,7 +41,14 @@ class RemoteProfileRepository(private val container: AppContainer) : ProfileRepo
override suspend fun signOut() { override suspend fun signOut() {
val context = container.context val context = container.context
container.context.settings.updateData { context.settings.updateData {
it
.toBuilder()
.clear()
.build()
}
context.cache.updateData {
it it
.toBuilder() .toBuilder()
.clear() .clear()
@@ -48,12 +56,5 @@ class RemoteProfileRepository(private val container: AppContainer) : ProfileRepo
} }
context.app.events.signOut.next(Unit) context.app.events.signOut.next(Unit)
// context.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
// val pm = context.packageManager
// val intent = pm.getLaunchIntentForPackage(context.packageName)
// val mainIntent = Intent.makeRestartActivityTask(intent?.component)
// context.startActivity(mainIntent)
// Runtime.getRuntime().exit(0)
} }
} }

View File

@@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.Application import ru.n08i40k.polytechnic.next.Application
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.screen.MainScreen import ru.n08i40k.polytechnic.next.ui.screen.MainScreen
import ru.n08i40k.polytechnic.next.ui.screen.auth.AuthScreen import ru.n08i40k.polytechnic.next.ui.screen.auth.AuthScreen
import ru.n08i40k.polytechnic.next.utils.app import ru.n08i40k.polytechnic.next.utils.app

View File

@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.app.AppContainer import ru.n08i40k.polytechnic.next.app.AppContainer
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
import ru.n08i40k.polytechnic.next.utils.MyResult import ru.n08i40k.polytechnic.next.utils.MyResult
@@ -25,7 +25,7 @@ sealed interface GroupUiState {
data class HasData( data class HasData(
val group: GroupOrTeacher, val group: GroupOrTeacher,
val updateDates: UpdateDates, val cacheDate: CacheDate,
val lastUpdateAt: Long, val lastUpdateAt: Long,
override val isLoading: Boolean override val isLoading: Boolean
) : GroupUiState ) : GroupUiState
@@ -33,7 +33,7 @@ sealed interface GroupUiState {
private data class GroupViewModelState( private data class GroupViewModelState(
val group: GroupOrTeacher? = null, val group: GroupOrTeacher? = null,
val updateDates: UpdateDates? = null, val updateDates: CacheDate? = null,
val lastUpdateAt: Long = 0, val lastUpdateAt: Long = 0,
val isLoading: Boolean = false val isLoading: Boolean = false
) { ) {

View File

@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.app.AppContainer import ru.n08i40k.polytechnic.next.app.AppContainer
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
import ru.n08i40k.polytechnic.next.utils.MyResult import ru.n08i40k.polytechnic.next.utils.MyResult
@@ -25,7 +25,7 @@ sealed interface SearchUiState {
data class HasData( data class HasData(
val teacher: GroupOrTeacher, val teacher: GroupOrTeacher,
val updateDates: UpdateDates, val cacheDate: CacheDate,
val lastUpdateAt: Long, val lastUpdateAt: Long,
override val isLoading: Boolean override val isLoading: Boolean
) : SearchUiState ) : SearchUiState
@@ -33,7 +33,7 @@ sealed interface SearchUiState {
private data class SearchViewModelState( private data class SearchViewModelState(
val teacher: GroupOrTeacher? = null, val teacher: GroupOrTeacher? = null,
val updateDates: UpdateDates? = null, val updateDates: CacheDate? = null,
val lastUpdateAt: Long = 0, val lastUpdateAt: Long = 0,
val isLoading: Boolean = false val isLoading: Boolean = false
) { ) {

View File

@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.app.AppContainer import ru.n08i40k.polytechnic.next.app.AppContainer
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
import ru.n08i40k.polytechnic.next.utils.MyResult import ru.n08i40k.polytechnic.next.utils.MyResult
@@ -24,7 +24,7 @@ sealed interface TeacherUiState {
data class HasData( data class HasData(
val teacher: GroupOrTeacher, val teacher: GroupOrTeacher,
val updateDates: UpdateDates, val cacheDate: CacheDate,
val lastUpdateAt: Long, val lastUpdateAt: Long,
override val isLoading: Boolean override val isLoading: Boolean
) : TeacherUiState ) : TeacherUiState
@@ -32,7 +32,7 @@ sealed interface TeacherUiState {
private data class TeacherViewModelState( private data class TeacherViewModelState(
val teacher: GroupOrTeacher? = null, val teacher: GroupOrTeacher? = null,
val updateDates: UpdateDates? = null, val updateDates: CacheDate? = null,
val lastUpdateAt: Long = 0, val lastUpdateAt: Long = 0,
val isLoading: Boolean = false val isLoading: Boolean = false
) { ) {

View File

@@ -38,7 +38,7 @@ import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.Application import ru.n08i40k.polytechnic.next.Application
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.model.UserRole import ru.n08i40k.polytechnic.next.model.UserRole
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.AppRoute import ru.n08i40k.polytechnic.next.ui.AppRoute
import ru.n08i40k.polytechnic.next.ui.icons.AppIcons import ru.n08i40k.polytechnic.next.ui.icons.AppIcons
import ru.n08i40k.polytechnic.next.ui.icons.appicons.Filled import ru.n08i40k.polytechnic.next.ui.icons.appicons.Filled

View File

@@ -35,6 +35,8 @@ import androidx.navigation.compose.rememberNavController
import com.google.android.gms.tasks.OnCompleteListener import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.Task
import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessaging
import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.proto.cache
import ru.n08i40k.polytechnic.next.ui.AppRoute import ru.n08i40k.polytechnic.next.ui.AppRoute
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
import ru.n08i40k.polytechnic.next.ui.helper.SnackbarBox import ru.n08i40k.polytechnic.next.ui.helper.SnackbarBox
@@ -61,8 +63,7 @@ private fun FormWrapper(
with(localDensity) { with(localDensity) {
onWidthChange(it.size.width.toDp()) onWidthChange(it.size.width.toDp())
} }
} },
/*.animateContentSize()*/,
content = content content = content
) )
} }
@@ -84,6 +85,8 @@ private fun AuthForm(parentNavController: NavController, pushSnackbar: PushSnack
} }
val finish: () -> Unit = { val finish: () -> Unit = {
runBlocking { context.cache.updateData { it.toBuilder().clear().build() } }
parentNavController.navigate(AppRoute.MAIN.route) { parentNavController.navigate(AppRoute.MAIN.route) {
popUpTo(AppRoute.AUTH.route) { inclusive = true } popUpTo(AppRoute.AUTH.route) { inclusive = true }
} }

View File

@@ -40,7 +40,7 @@ import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignIn import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignIn
import ru.n08i40k.polytechnic.next.network.unwrapException import ru.n08i40k.polytechnic.next.network.unwrapException
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
import java.util.logging.Logger import java.util.logging.Logger

View File

@@ -8,7 +8,7 @@ import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignInVK import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignInVK
import ru.n08i40k.polytechnic.next.network.unwrapException import ru.n08i40k.polytechnic.next.network.unwrapException
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.widgets.OneTapComplete import ru.n08i40k.polytechnic.next.ui.widgets.OneTapComplete
import java.util.logging.Logger import java.util.logging.Logger

View File

@@ -42,7 +42,7 @@ import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.model.UserRole import ru.n08i40k.polytechnic.next.model.UserRole
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUp import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUp
import ru.n08i40k.polytechnic.next.network.unwrapException import ru.n08i40k.polytechnic.next.network.unwrapException
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector

View File

@@ -41,7 +41,7 @@ import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.model.UserRole import ru.n08i40k.polytechnic.next.model.UserRole
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUpVK import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUpVK
import ru.n08i40k.polytechnic.next.network.unwrapException import ru.n08i40k.polytechnic.next.network.unwrapException
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector

View File

@@ -65,7 +65,7 @@ fun GroupScheduleScreen(viewModel: GroupViewModel) {
Column { Column {
val data = uiState as GroupUiState.HasData val data = uiState as GroupUiState.HasData
UpdateInfo(data.lastUpdateAt, data.updateDates) UpdateInfo(data.lastUpdateAt, data.cacheDate)
Spacer(Modifier.height(10.dp)) Spacer(Modifier.height(10.dp))
SchedulePager(data.group) SchedulePager(data.group)
} }

View File

@@ -65,7 +65,7 @@ fun TeacherScheduleScreen(viewModel: TeacherViewModel) {
Column { Column {
val data = uiState as TeacherUiState.HasData val data = uiState as TeacherUiState.HasData
UpdateInfo(data.lastUpdateAt, data.updateDates) UpdateInfo(data.lastUpdateAt, data.cacheDate)
Spacer(Modifier.height(10.dp)) Spacer(Modifier.height(10.dp))
SchedulePager(data.teacher) SchedulePager(data.teacher)
} }

View File

@@ -85,7 +85,7 @@ fun TeacherSearchScreen(viewModel: SearchViewModel) {
Column { Column {
val data = uiState as SearchUiState.HasData val data = uiState as SearchUiState.HasData
UpdateInfo(data.lastUpdateAt, data.updateDates) UpdateInfo(data.lastUpdateAt, data.cacheDate)
Spacer(Modifier.height(10.dp)) Spacer(Modifier.height(10.dp))
SchedulePager(data.teacher) SchedulePager(data.teacher)
} }

View File

@@ -19,8 +19,8 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
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 ru.n08i40k.polytechnic.next.CacheDate
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.UpdateDates
import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCard import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCard
import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCardTitle import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCardTitle
import ru.n08i40k.polytechnic.next.utils.* import ru.n08i40k.polytechnic.next.utils.*
@@ -32,15 +32,15 @@ val expanded = mutableStateOf(false)
@Composable @Composable
fun UpdateInfo( fun UpdateInfo(
lastUpdateAt: Long = 0, lastUpdateAt: Long = 0,
updateDates: UpdateDates = UpdateDates.newBuilder().build() cacheDate: CacheDate = CacheDate.newBuilder().build()
) { ) {
var expanded by remember { expanded } var expanded by remember { expanded }
val format = "HH:mm:ss dd.MM.yyyy" val format = "HH:mm:ss dd.MM.yyyy"
val currentDate = Date(lastUpdateAt).toString(format) val currentDate = Date(lastUpdateAt).toString(format)
val cacheUpdateDate = Date(updateDates.cache).toString(format) val cacheUpdateDate = Date(cacheDate.cache).toString(format)
val scheduleUpdateDate = Date(updateDates.schedule).toString(format) val scheduleUpdateDate = Date(cacheDate.schedule).toString(format)
ExpandableCard( ExpandableCard(
expanded = expanded, expanded = expanded,

View File

@@ -18,9 +18,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@@ -34,6 +32,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.model.Day import ru.n08i40k.polytechnic.next.model.Day
import ru.n08i40k.polytechnic.next.model.Lesson
import ru.n08i40k.polytechnic.next.model.LessonType import ru.n08i40k.polytechnic.next.model.LessonType
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
import ru.n08i40k.polytechnic.next.utils.dateTime import ru.n08i40k.polytechnic.next.utils.dateTime
@@ -80,7 +79,8 @@ private fun getCurrentLessonIdx(day: Day?): Flow<Int> {
@Composable @Composable
fun DayCard( fun DayCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
day: Day = MockScheduleRepository.exampleTeacher.days[0] day: Day = MockScheduleRepository.exampleTeacher.days[0],
onLessonClick: (Lesson) -> Unit = {},
) { ) {
val offset = remember(day) { getDayOffset(day) } val offset = remember(day) { getDayOffset(day) }
@@ -119,9 +119,9 @@ fun DayCard(
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleLarge
) )
if (day.street != null) { day.street?.let {
Text( Text(
day.street, it,
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
@@ -177,12 +177,9 @@ fun DayCard(
LessonType.EXAM_DEFAULT -> examCardColors LessonType.EXAM_DEFAULT -> examCardColors
} }
// TODO: Вернуть ExtraInfo
var extraInfo by remember { mutableStateOf(false) }
Box( Box(
Modifier Modifier
.clickable { extraInfo = true } .clickable { onLessonClick(lesson) }
.background(cardColors.containerColor) .background(cardColors.containerColor)
) { ) {
val modifier = val modifier =

View File

@@ -0,0 +1,150 @@
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.compose.ui.window.Dialog
import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.model.Lesson
import ru.n08i40k.polytechnic.next.model.LessonType
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
import ru.n08i40k.polytechnic.next.utils.dayMinutes
import ru.n08i40k.polytechnic.next.utils.fmtAsClock
class LessonPreviewParameterProvider : PreviewParameterProvider<Lesson> {
override val values: Sequence<Lesson>
get() {
val lessons = MockScheduleRepository.exampleGroup.days[0].lessons
return sequenceOf(
lessons[0],
lessons[2],
lessons[4],
lessons[6],
)
}
}
@Preview
@Composable
private fun ExtraInfoDialogPreview(
@PreviewParameter(LessonPreviewParameterProvider::class) lesson: Lesson
) {
ExtraInfoDialog(lesson) { }
}
@Composable
fun ExtraInfoDialog(
lesson: Lesson,
onDismiss: () -> Unit
) {
Dialog(onDismiss) {
Card {
Column(Modifier.padding(10.dp)) {
var minWidth by remember { mutableStateOf(Dp.Unspecified) }
val density = LocalDensity.current
@Composable
fun kvText(title: String, text: String) {
Row(
Modifier.alpha(if (minWidth == Dp.Unspecified) 0f else 1f),
verticalAlignment = Alignment.CenterVertically
) {
Text(
title,
Modifier
.onGloballyPositioned {
with(density) {
val dp = it.size.width.toDp()
minWidth =
if (minWidth == Dp.Unspecified)
dp
else
max(minWidth, dp)
}
}
.size(minWidth, Dp.Unspecified),
fontWeight = FontWeight.Bold,
fontFamily = FontFamily.Monospace,
textAlign = TextAlign.Right,
)
Text(text)
}
}
kvText(stringResource(R.string.extra_info_lesson_name), lesson.name ?: "")
when (lesson.type) {
LessonType.BREAK -> throw IllegalArgumentException()
LessonType.DEFAULT -> null
LessonType.ADDITIONAL -> null
LessonType.CONSULTATION -> R.string.lesson_type_consultation
LessonType.INDEPENDENT_WORK -> R.string.lesson_type_independent_work
LessonType.EXAM -> R.string.lesson_type_exam
LessonType.EXAM_WITH_GRADE -> R.string.lesson_type_exam_with_grade
LessonType.EXAM_DEFAULT -> R.string.lesson_type_exam_default
}?.let {
kvText(stringResource(R.string.extra_info_type), stringResource(it))
}
if (lesson.subGroups.size == 1) {
kvText(
stringResource(R.string.extra_info_teacher),
stringResource(
R.string.extra_info_teacher_second,
lesson.subGroups[0].teacher,
lesson.subGroups[0].cabinet
)
)
} else {
for (subGroup in lesson.subGroups) {
kvText(
stringResource(R.string.extra_info_teacher),
stringResource(
R.string.extra_info_teacher_second_subgroup,
subGroup.teacher,
subGroup.cabinet,
subGroup.number
)
)
}
}
kvText(
stringResource(R.string.extra_info_duration),
stringResource(
R.string.extra_info_duration_second,
lesson.time.start.dayMinutes.fmtAsClock(),
lesson.time.end.dayMinutes.fmtAsClock(),
lesson.duration / 60,
lesson.duration % 60
)
)
}
}
}
}

View File

@@ -7,6 +7,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
@@ -17,10 +21,13 @@ import androidx.compose.ui.util.lerp
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
import ru.n08i40k.polytechnic.next.model.Lesson
import ru.n08i40k.polytechnic.next.model.LessonType
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
import ru.n08i40k.polytechnic.next.ui.widgets.NotificationCard import ru.n08i40k.polytechnic.next.ui.widgets.NotificationCard
import ru.n08i40k.polytechnic.next.utils.dateTime import ru.n08i40k.polytechnic.next.utils.dateTime
import ru.n08i40k.polytechnic.next.utils.now import ru.n08i40k.polytechnic.next.utils.now
import java.lang.ref.WeakReference
import java.util.logging.Level import java.util.logging.Level
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@@ -40,6 +47,8 @@ fun SchedulePager(schedule: GroupOrTeacher = MockScheduleRepository.exampleTeach
pageCount = { schedule.days.size } pageCount = { schedule.days.size }
) )
var dialogLesson by remember { mutableStateOf<WeakReference<Lesson>?>(null) }
Column { Column {
if (isScheduleOutdated(schedule)) if (isScheduleOutdated(schedule))
NotificationCard(Level.WARNING, stringResource(R.string.outdated_schedule)) NotificationCard(Level.WARNING, stringResource(R.string.outdated_schedule))
@@ -69,7 +78,14 @@ fun SchedulePager(schedule: GroupOrTeacher = MockScheduleRepository.exampleTeach
) )
}, },
schedule.days[page] schedule.days[page]
) ) { dialogLesson = WeakReference(it) }
} }
} }
dialogLesson?.get()?.let { lesson ->
if (lesson.type == LessonType.BREAK)
return@let
ExtraInfoDialog(lesson) { dialogLesson = null }
}
} }

View File

@@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import ru.n08i40k.polytechnic.next.app.appContainer import ru.n08i40k.polytechnic.next.app.appContainer
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import ru.n08i40k.polytechnic.next.utils.MyResult import ru.n08i40k.polytechnic.next.utils.MyResult
import java.time.Duration import java.time.Duration
import java.util.logging.Logger import java.util.logging.Logger

View File

@@ -8,7 +8,7 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import ru.n08i40k.polytechnic.next.app.appContainer import ru.n08i40k.polytechnic.next.app.appContainer
import ru.n08i40k.polytechnic.next.settings.settings import ru.n08i40k.polytechnic.next.proto.settings
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.logging.Logger import java.util.logging.Logger

View File

@@ -0,0 +1,19 @@
syntax = "proto3";
option java_package = "ru.n08i40k.polytechnic.next";
option java_multiple_files = true;
message CacheResponse {
string hash = 1;
string data = 2;
}
message CacheDate {
int64 cache = 1;
int64 schedule = 2;
}
message Cache {
map<string, CacheResponse> storage = 4;
CacheDate date = 5;
}

View File

@@ -0,0 +1,15 @@
syntax = "proto3";
option java_package = "ru.n08i40k.polytechnic.next";
option java_multiple_files = true;
message SettingsV2 {
string user_id = 1;
string access_token = 2;
string group = 3;
string version = 5;
string suppressed_version = 6;
string fcm_token = 7;
}

View File

@@ -100,4 +100,11 @@
<string name="day_view_end_description">Ура, можно идти домой! Наверное :(</string> <string name="day_view_end_description">Ура, можно идти домой! Наверное :(</string>
<string name="day_view_channel_name">Текущая пара</string> <string name="day_view_channel_name">Текущая пара</string>
<string name="day_view_channel_description">Отображает текущую пару или перемену в уведомлении</string> <string name="day_view_channel_description">Отображает текущую пару или перемену в уведомлении</string>
<string name="extra_info_lesson_name">"Название: "</string>
<string name="extra_info_teacher">"Преподаватель: "</string>
<string name="extra_info_teacher_second">%1$s в %2$s каб.</string>
<string name="extra_info_teacher_second_subgroup">%1$s в %2$s каб. [подгруппа - %3$d]</string>
<string name="extra_info_duration">"Длительность: "</string>
<string name="extra_info_duration_second">С %1$s до %2$s (%3$d ч. %4$d мин.)</string>
<string name="extra_info_type">"Тип: "</string>
</resources> </resources>

View File

@@ -100,4 +100,11 @@
<string name="day_view_end_description">ya ne budu eto perevidit\'</string> <string name="day_view_end_description">ya ne budu eto perevidit\'</string>
<string name="day_view_channel_name">Current lesson</string> <string name="day_view_channel_name">Current lesson</string>
<string name="day_view_channel_description">View the current lesson and breaks in notification</string> <string name="day_view_channel_description">View the current lesson and breaks in notification</string>
<string name="extra_info_lesson_name">"Name: "</string>
<string name="extra_info_teacher">"Teacher: "</string>
<string name="extra_info_teacher_second">%1$s in %2$s cab.</string>
<string name="extra_info_teacher_second_subgroup">%1$s in %2$s cab. [subgroup - %3$d]</string>
<string name="extra_info_duration">"Duration: "</string>
<string name="extra_info_duration_second">From %1$s to %2$s (%3$d h. %4$d min.)</string>
<string name="extra_info_type">"Type: "</string>
</resources> </resources>

View File

@@ -1,27 +1,28 @@
[versions] [versions]
agp = "8.8.1" agp = "8.8.2"
desugar_jdk_libs = "2.1.4" desugar_jdk_libs = "2.1.5"
kotlin = "2.1.10" kotlin = "2.1.10"
coreKtx = "1.15.0" coreKtx = "1.15.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.2.1"
espressoCore = "3.6.1" espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7" lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.0" activityCompose = "1.10.1"
composeBom = "2025.02.00" composeBom = "2025.03.00"
accompanistSwiperefresh = "0.36.0" accompanistSwiperefresh = "0.36.0"
firebaseBom = "33.9.0" firebaseBom = "33.10.0"
hiltAndroid = "2.55" hiltAndroid = "2.55"
hiltAndroidCompiler = "2.55" hiltAndroidCompiler = "2.55"
hiltNavigationCompose = "1.2.0" hiltNavigationCompose = "1.2.0"
kotlinxSerializationJson = "1.8.0" kotlinxSerializationJson = "1.8.0"
protobufLite = "3.0.1" protobufLite = "3.0.1"
volley = "1.2.1" volley = "1.2.1"
datastore = "1.1.2" datastore = "1.1.3"
navigationCompose = "2.8.9" navigationCompose = "2.8.9"
googleFirebaseCrashlytics = "3.0.3" googleFirebaseCrashlytics = "3.0.3"
workRuntime = "2.10.0" workRuntime = "2.10.0"
vkid = "2.3.1" #noinspection GradleDependency
vkid = "2.2.2"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -59,8 +60,8 @@ volley = { group = "com.android.volley", name = "volley", version.ref = "volley"
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
firebase-analytics = { module = "com.google.firebase:firebase-analytics", version = "22.2.0" } firebase-analytics = { module = "com.google.firebase:firebase-analytics", version = "22.3.0" }
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version = "19.4.0" } firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version = "19.4.1" }
firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version = "24.1.0" } firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version = "24.1.0" }
firebase-config = { group = "com.google.firebase", name = "firebase-config", version = "22.1.0" } firebase-config = { group = "com.google.firebase", name = "firebase-config", version = "22.1.0" }
vk-vkid = {group = "com.vk.id", name = "vkid", version.ref = "vkid" } vk-vkid = {group = "com.vk.id", name = "vkid", version.ref = "vkid" }