add offline mode
parent
d31bce2f89
commit
0074e651d0
|
@ -10,6 +10,14 @@
|
|||
</SelectionState>
|
||||
<SelectionState runConfigName="MainActivity">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2024-07-09T21:11:01.409507Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/ernestlitvinenko/.android/avd/Small_Phone_API_28.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
id("com.google.gms.google-services")
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.kotlinx.serialization)
|
||||
id("com.apollographql.apollo3").version("3.8.3")
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -50,8 +52,13 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
apollo {
|
||||
service("service") {
|
||||
packageName.set("com.example.mpdriver")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
|
@ -76,4 +83,8 @@ dependencies {
|
|||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.yandex.maps)
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
|
||||
implementation("com.apollographql.apollo3:apollo-runtime:3.8.3")
|
||||
implementation("ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0")
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
query fetchApplicationData ($userID: String!) {
|
||||
tasks(userId: $userID) {
|
||||
id
|
||||
startPln
|
||||
endPln
|
||||
startFact
|
||||
endFact
|
||||
status
|
||||
taskType
|
||||
text
|
||||
|
||||
events {
|
||||
id
|
||||
}
|
||||
|
||||
subtasks {
|
||||
id
|
||||
}
|
||||
|
||||
route {
|
||||
temperatureProperty
|
||||
name
|
||||
|
||||
trailer {
|
||||
gost
|
||||
}
|
||||
|
||||
truck {
|
||||
gost
|
||||
}
|
||||
}
|
||||
}
|
||||
subtasks(userId: $userID) {
|
||||
id
|
||||
startPln
|
||||
endPln
|
||||
startFact
|
||||
endFact
|
||||
status
|
||||
taskType
|
||||
text
|
||||
station {
|
||||
name
|
||||
location {
|
||||
lat
|
||||
lon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notes(userId: $userID) {
|
||||
id
|
||||
taskId
|
||||
noteStatus
|
||||
tip
|
||||
text
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
query GetActiveSubtaskID($userID: String!) {
|
||||
task(userId: $userID, isActive: true) {
|
||||
activeSubtask {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
query GetActiveTaskId($userID: String!) {
|
||||
task(isActive: true, userId: $userID) {
|
||||
id
|
||||
}
|
||||
completedTasks: tasks(userId: $userID, isCompleted: true) {
|
||||
id
|
||||
startPln
|
||||
}
|
||||
|
||||
plannedTasks: tasks(userId: $userID, isPlanned: true) {
|
||||
id,
|
||||
startPln
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
query GetPlannedTasksIDs($userID: String!) {
|
||||
tasks(userId: $userID, isPlanned:true) {
|
||||
id
|
||||
subtasks {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
query GetSubtaskByID($userID: String!, $subtaskID: String!) {
|
||||
subtask(userId: $userID, subtaskId: $subtaskID) {
|
||||
id
|
||||
text
|
||||
startPln
|
||||
endPln
|
||||
status
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
query GetTaskById($taskID: String!, $userID: String!) {
|
||||
task(userId: $userID, taskId: $taskID) {
|
||||
status
|
||||
route {
|
||||
name
|
||||
temperatureProperty
|
||||
truck {
|
||||
gost
|
||||
}
|
||||
trailer {
|
||||
gost
|
||||
}
|
||||
}
|
||||
text
|
||||
startPln
|
||||
endPln
|
||||
startFact
|
||||
endFact
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
query GetTasksFeedScreen($userID: String!){
|
||||
tasks(userId: $userID) {
|
||||
id
|
||||
text
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,453 @@
|
|||
type Query {
|
||||
tasks(userId: String!, isPlanned: Boolean = false, isCompleted: Boolean = false): [AppTaskQL!]!
|
||||
|
||||
task(userId: String!, taskId: String = null, isActive: Boolean = null): AppTaskQL
|
||||
|
||||
countPlannedTasks(userId: String!): Int!
|
||||
|
||||
countCompletedTasks(userId: String!): Int!
|
||||
|
||||
notes(userId: String!): [AppNoteQL!]!
|
||||
|
||||
subtask(userId: String!, subtaskId: String!): SubtaskQL
|
||||
|
||||
subtasks(userId: String!): [SubtaskQL!]!
|
||||
}
|
||||
|
||||
type AppTaskQL {
|
||||
activeSubtask: SubtaskQL
|
||||
|
||||
id: String!
|
||||
|
||||
profileId: Int!
|
||||
|
||||
startPln: DateTime!
|
||||
|
||||
endPln: DateTime!
|
||||
|
||||
startFact: DateTime
|
||||
|
||||
endFact: DateTime
|
||||
|
||||
status: StatusEnumQl!
|
||||
|
||||
taskType: String!
|
||||
|
||||
text: String!
|
||||
|
||||
events: [AppEventQL!]!
|
||||
|
||||
subtasks: [SubtaskQL!]!
|
||||
|
||||
route: AppRouteQL!
|
||||
}
|
||||
|
||||
type SubtaskQL {
|
||||
id: String!
|
||||
|
||||
startPln: DateTime!
|
||||
|
||||
endPln: DateTime!
|
||||
|
||||
startFact: DateTime
|
||||
|
||||
endFact: DateTime
|
||||
|
||||
status: StatusEnumQl!
|
||||
|
||||
taskType: String!
|
||||
|
||||
text: String!
|
||||
|
||||
station: MSTQL
|
||||
}
|
||||
|
||||
"""
|
||||
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
|
||||
"""
|
||||
scalar String
|
||||
|
||||
"""
|
||||
Date with time (isoformat)
|
||||
"""
|
||||
scalar DateTime
|
||||
|
||||
enum StatusEnumQl {
|
||||
CANCELLED
|
||||
|
||||
IN_PROGRESS
|
||||
|
||||
COMPLETED
|
||||
|
||||
NOT_DEFINED
|
||||
}
|
||||
|
||||
type MSTQL {
|
||||
name: String!
|
||||
|
||||
location: LocationQL!
|
||||
}
|
||||
|
||||
type LocationQL {
|
||||
lat: Float!
|
||||
|
||||
lon: Float!
|
||||
}
|
||||
|
||||
"""
|
||||
The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).
|
||||
"""
|
||||
scalar Float
|
||||
|
||||
"""
|
||||
The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
|
||||
"""
|
||||
scalar Int
|
||||
|
||||
type AppEventQL {
|
||||
id: String!
|
||||
|
||||
type: String!
|
||||
|
||||
text: String!
|
||||
|
||||
eventDatetime: DateTime!
|
||||
}
|
||||
|
||||
type AppRouteQL {
|
||||
temperatureProperty: MarshTemperaturePropertyQL!
|
||||
|
||||
name: String!
|
||||
|
||||
trailer: TRSQL
|
||||
|
||||
truck: TRSQL
|
||||
}
|
||||
|
||||
enum MarshTemperaturePropertyQL {
|
||||
HOT
|
||||
|
||||
COLD
|
||||
|
||||
UNDEFINED
|
||||
}
|
||||
|
||||
type TRSQL {
|
||||
gost: String
|
||||
}
|
||||
|
||||
"""
|
||||
The `Boolean` scalar type represents `true` or `false`.
|
||||
"""
|
||||
scalar Boolean
|
||||
|
||||
type AppNoteQL {
|
||||
id: String!
|
||||
|
||||
userId: String!
|
||||
|
||||
taskId: String!
|
||||
|
||||
noteStatus: Int!
|
||||
|
||||
tip: Int!
|
||||
|
||||
text: String!
|
||||
}
|
||||
|
||||
"""
|
||||
A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.
|
||||
"""
|
||||
type __Schema {
|
||||
description: String
|
||||
|
||||
"""
|
||||
A list of all types supported by this server.
|
||||
"""
|
||||
types: [__Type!]!
|
||||
|
||||
"""
|
||||
The type that query operations will be rooted at.
|
||||
"""
|
||||
queryType: __Type!
|
||||
|
||||
"""
|
||||
If this server supports mutation, the type that mutation operations will be rooted at.
|
||||
"""
|
||||
mutationType: __Type
|
||||
|
||||
"""
|
||||
If this server support subscription, the type that subscription operations will be rooted at.
|
||||
"""
|
||||
subscriptionType: __Type
|
||||
|
||||
"""
|
||||
A list of all directives supported by this server.
|
||||
"""
|
||||
directives: [__Directive!]!
|
||||
}
|
||||
|
||||
"""
|
||||
The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
|
||||
|
||||
Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByURL`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
|
||||
"""
|
||||
type __Type {
|
||||
kind: __TypeKind!
|
||||
|
||||
name: String
|
||||
|
||||
description: String
|
||||
|
||||
specifiedByURL: String
|
||||
|
||||
fields(includeDeprecated: Boolean = false): [__Field!]
|
||||
|
||||
interfaces: [__Type!]
|
||||
|
||||
possibleTypes: [__Type!]
|
||||
|
||||
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
|
||||
|
||||
inputFields(includeDeprecated: Boolean = false): [__InputValue!]
|
||||
|
||||
ofType: __Type
|
||||
|
||||
isOneOf: Boolean
|
||||
}
|
||||
|
||||
"""
|
||||
An enum describing what kind of type a given `__Type` is.
|
||||
"""
|
||||
enum __TypeKind {
|
||||
"""
|
||||
Indicates this type is a scalar.
|
||||
"""
|
||||
SCALAR
|
||||
|
||||
"""
|
||||
Indicates this type is an object. `fields` and `interfaces` are valid fields.
|
||||
"""
|
||||
OBJECT
|
||||
|
||||
"""
|
||||
Indicates this type is an interface. `fields`, `interfaces`, and `possibleTypes` are valid fields.
|
||||
"""
|
||||
INTERFACE
|
||||
|
||||
"""
|
||||
Indicates this type is a union. `possibleTypes` is a valid field.
|
||||
"""
|
||||
UNION
|
||||
|
||||
"""
|
||||
Indicates this type is an enum. `enumValues` is a valid field.
|
||||
"""
|
||||
ENUM
|
||||
|
||||
"""
|
||||
Indicates this type is an input object. `inputFields` is a valid field.
|
||||
"""
|
||||
INPUT_OBJECT
|
||||
|
||||
"""
|
||||
Indicates this type is a list. `ofType` is a valid field.
|
||||
"""
|
||||
LIST
|
||||
|
||||
"""
|
||||
Indicates this type is a non-null. `ofType` is a valid field.
|
||||
"""
|
||||
NON_NULL
|
||||
}
|
||||
|
||||
"""
|
||||
Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.
|
||||
"""
|
||||
type __Field {
|
||||
name: String!
|
||||
|
||||
description: String
|
||||
|
||||
args(includeDeprecated: Boolean = false): [__InputValue!]!
|
||||
|
||||
type: __Type!
|
||||
|
||||
isDeprecated: Boolean!
|
||||
|
||||
deprecationReason: String
|
||||
}
|
||||
|
||||
"""
|
||||
Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.
|
||||
"""
|
||||
type __InputValue {
|
||||
name: String!
|
||||
|
||||
description: String
|
||||
|
||||
type: __Type!
|
||||
|
||||
"""
|
||||
A GraphQL-formatted string representing the default value for this input value.
|
||||
"""
|
||||
defaultValue: String
|
||||
|
||||
isDeprecated: Boolean!
|
||||
|
||||
deprecationReason: String
|
||||
}
|
||||
|
||||
"""
|
||||
One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.
|
||||
"""
|
||||
type __EnumValue {
|
||||
name: String!
|
||||
|
||||
description: String
|
||||
|
||||
isDeprecated: Boolean!
|
||||
|
||||
deprecationReason: String
|
||||
}
|
||||
|
||||
"""
|
||||
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
|
||||
|
||||
In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.
|
||||
"""
|
||||
type __Directive {
|
||||
name: String!
|
||||
|
||||
description: String
|
||||
|
||||
isRepeatable: Boolean!
|
||||
|
||||
locations: [__DirectiveLocation!]!
|
||||
|
||||
args(includeDeprecated: Boolean = false): [__InputValue!]!
|
||||
}
|
||||
|
||||
"""
|
||||
A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.
|
||||
"""
|
||||
enum __DirectiveLocation {
|
||||
"""
|
||||
Location adjacent to a query operation.
|
||||
"""
|
||||
QUERY
|
||||
|
||||
"""
|
||||
Location adjacent to a mutation operation.
|
||||
"""
|
||||
MUTATION
|
||||
|
||||
"""
|
||||
Location adjacent to a subscription operation.
|
||||
"""
|
||||
SUBSCRIPTION
|
||||
|
||||
"""
|
||||
Location adjacent to a field.
|
||||
"""
|
||||
FIELD
|
||||
|
||||
"""
|
||||
Location adjacent to a fragment definition.
|
||||
"""
|
||||
FRAGMENT_DEFINITION
|
||||
|
||||
"""
|
||||
Location adjacent to a fragment spread.
|
||||
"""
|
||||
FRAGMENT_SPREAD
|
||||
|
||||
"""
|
||||
Location adjacent to an inline fragment.
|
||||
"""
|
||||
INLINE_FRAGMENT
|
||||
|
||||
"""
|
||||
Location adjacent to a variable definition.
|
||||
"""
|
||||
VARIABLE_DEFINITION
|
||||
|
||||
"""
|
||||
Location adjacent to a schema definition.
|
||||
"""
|
||||
SCHEMA
|
||||
|
||||
"""
|
||||
Location adjacent to a scalar definition.
|
||||
"""
|
||||
SCALAR
|
||||
|
||||
"""
|
||||
Location adjacent to an object type definition.
|
||||
"""
|
||||
OBJECT
|
||||
|
||||
"""
|
||||
Location adjacent to a field definition.
|
||||
"""
|
||||
FIELD_DEFINITION
|
||||
|
||||
"""
|
||||
Location adjacent to an argument definition.
|
||||
"""
|
||||
ARGUMENT_DEFINITION
|
||||
|
||||
"""
|
||||
Location adjacent to an interface definition.
|
||||
"""
|
||||
INTERFACE
|
||||
|
||||
"""
|
||||
Location adjacent to a union definition.
|
||||
"""
|
||||
UNION
|
||||
|
||||
"""
|
||||
Location adjacent to an enum definition.
|
||||
"""
|
||||
ENUM
|
||||
|
||||
"""
|
||||
Location adjacent to an enum value definition.
|
||||
"""
|
||||
ENUM_VALUE
|
||||
|
||||
"""
|
||||
Location adjacent to an input object type definition.
|
||||
"""
|
||||
INPUT_OBJECT
|
||||
|
||||
"""
|
||||
Location adjacent to an input object field definition.
|
||||
"""
|
||||
INPUT_FIELD_DEFINITION
|
||||
}
|
||||
|
||||
"""
|
||||
Directs the executor to include this field or fragment only when the `if` argument is true.
|
||||
"""
|
||||
directive @include ("Included when true." if: Boolean!) on FIELD|FRAGMENT_SPREAD|INLINE_FRAGMENT
|
||||
|
||||
"""
|
||||
Directs the executor to skip this field or fragment when the `if` argument is true.
|
||||
"""
|
||||
directive @skip ("Skipped when true." if: Boolean!) on FIELD|FRAGMENT_SPREAD|INLINE_FRAGMENT
|
||||
|
||||
"""
|
||||
Marks an element of a GraphQL schema as no longer supported.
|
||||
"""
|
||||
directive @deprecated ("Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https:\/\/commonmark.org\/)." reason: String = "No longer supported") on FIELD_DEFINITION|ARGUMENT_DEFINITION|INPUT_FIELD_DEFINITION|ENUM_VALUE
|
||||
|
||||
"""
|
||||
Exposes a URL that specifies the behaviour of this scalar.
|
||||
"""
|
||||
directive @specifiedBy ("The URL that specifies the behaviour of this scalar." url: String!) on SCALAR
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.example.mpdriver
|
|||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
|
@ -22,6 +23,7 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
@ -30,6 +32,7 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.sharp.Settings
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
@ -37,9 +40,11 @@ import androidx.compose.material3.Scaffold
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -49,6 +54,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
|
@ -58,9 +64,16 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.mpdriver.api.Api
|
||||
import com.example.mpdriver.api.TaskApi
|
||||
import com.example.mpdriver.api.apolloClient
|
||||
import com.example.mpdriver.api.fetchAppDataToDB
|
||||
|
||||
|
||||
import com.example.mpdriver.api.toJson
|
||||
import com.example.mpdriver.recievers.TimeTickReciever
|
||||
import com.example.mpdriver.screens.Feed
|
||||
import com.example.mpdriver.screens.MapScreen
|
||||
import com.example.mpdriver.screens.NoteScreen
|
||||
import com.example.mpdriver.screens.PhoneCodeInputScreen
|
||||
import com.example.mpdriver.screens.PhoneInputScreen
|
||||
import com.example.mpdriver.screens.SubtaskScreen
|
||||
|
@ -68,6 +81,8 @@ import com.example.mpdriver.screens.TasksList
|
|||
import com.example.mpdriver.storage.Database
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.yandex.mapkit.MapKitFactory
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Dispatcher
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
@ -79,6 +94,8 @@ class MainActivity : ComponentActivity() {
|
|||
MapKitFactory.setApiKey("f4385b18-0740-454a-a71f-d20da7e8fc3b")
|
||||
MapKitFactory.initialize(this)
|
||||
val rootDir = MMKV.initialize(this)
|
||||
Log.d("MMKV.KEYS", Database.allKeys!!.joinToString())
|
||||
|
||||
println("mmkv root: $rootDir")
|
||||
registerReceiver(timeTickReciever, IntentFilter(Intent.ACTION_TIME_TICK))
|
||||
|
||||
|
@ -123,7 +140,32 @@ fun MainNavigator() {
|
|||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
var loadingData by remember {
|
||||
mutableStateOf(true)
|
||||
}
|
||||
|
||||
|
||||
val bottomNavController = rememberNavController()
|
||||
|
||||
val api = TaskApi(LocalContext.current)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
api.send_task_status_chains(onResponse = {
|
||||
Database.dropUpdates()
|
||||
})
|
||||
apolloClient.fetchAppDataToDB()
|
||||
loadingData = false
|
||||
}
|
||||
|
||||
if (loadingData)
|
||||
return Row(Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column(Modifier.fillMaxWidth().padding(horizontal = 16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||
Text( text = "Происходит обновление даннных, Пожалуйста, подождите.", fontSize = 16.sp, textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Header(
|
||||
|
@ -132,9 +174,11 @@ fun MainNavigator() {
|
|||
backLink = backLink
|
||||
)
|
||||
},
|
||||
bottomBar = { Footer(
|
||||
hostController = bottomNavController
|
||||
) },
|
||||
bottomBar = {
|
||||
Footer(
|
||||
hostController = bottomNavController
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
||||
|
||||
|
@ -145,14 +189,19 @@ fun MainNavigator() {
|
|||
) {
|
||||
composable("feed",
|
||||
exitTransition = {
|
||||
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Left, tween(200))
|
||||
slideOutOfContainer(
|
||||
AnimatedContentTransitionScope.SlideDirection.Left,
|
||||
tween(200)
|
||||
)
|
||||
}
|
||||
) {
|
||||
) {
|
||||
backLink = false
|
||||
headerTitle = "Лента"
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
Feed(hostController = bottomNavController)
|
||||
}
|
||||
}
|
||||
|
@ -160,15 +209,18 @@ fun MainNavigator() {
|
|||
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
AnimatedContentTransitionScope.SlideDirection.Left, tween(200))
|
||||
AnimatedContentTransitionScope.SlideDirection.Left, tween(200)
|
||||
)
|
||||
}
|
||||
|
||||
) {
|
||||
) {
|
||||
backLink = true
|
||||
headerTitle = "Задачи"
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
TasksList(hostController = bottomNavController)
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +228,11 @@ fun MainNavigator() {
|
|||
backLink = false
|
||||
headerTitle = "События"
|
||||
|
||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
Text(text = "События")
|
||||
}
|
||||
|
||||
|
@ -184,31 +240,43 @@ fun MainNavigator() {
|
|||
composable("settings") {
|
||||
backLink = false
|
||||
headerTitle = "Настройки"
|
||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
Text(text = "Profile")
|
||||
}
|
||||
}
|
||||
composable("notifications") {
|
||||
backLink = false
|
||||
headerTitle = "Уведомления"
|
||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
||||
Text(text = "Notifications")
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
NoteScreen()
|
||||
}
|
||||
}
|
||||
composable("chat") {
|
||||
backLink = false
|
||||
headerTitle = "Чат"
|
||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
Text(text = "Chat")
|
||||
}
|
||||
}
|
||||
composable("map",
|
||||
enterTransition = {
|
||||
fadeIn()
|
||||
fadeIn()
|
||||
},
|
||||
exitTransition = {
|
||||
ExitTransition.None
|
||||
}) {
|
||||
ExitTransition.None
|
||||
}) {
|
||||
backLink = false
|
||||
headerTitle = "Карта"
|
||||
MapScreen()
|
||||
|
@ -218,15 +286,18 @@ fun MainNavigator() {
|
|||
}),
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
AnimatedContentTransitionScope.SlideDirection.Left, tween(200))
|
||||
AnimatedContentTransitionScope.SlideDirection.Left, tween(200)
|
||||
)
|
||||
}
|
||||
) {bse ->
|
||||
bse.arguments?.let {args ->
|
||||
) { bse ->
|
||||
bse.arguments?.let { args ->
|
||||
headerTitle = "Детали задачи"
|
||||
backLink = true
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()){
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
SubtaskScreen(args.getLong("taskId"))
|
||||
}
|
||||
return@composable
|
||||
|
@ -300,14 +371,19 @@ data class FooterNavMenuLabel(
|
|||
)
|
||||
|
||||
@Composable
|
||||
fun Footer(modifier: Modifier = Modifier,
|
||||
hostController: NavHostController
|
||||
fun Footer(
|
||||
modifier: Modifier = Modifier,
|
||||
hostController: NavHostController
|
||||
) {
|
||||
|
||||
val menuItems = mapOf<String, FooterNavMenuLabel>(
|
||||
"feed" to FooterNavMenuLabel("Лента", R.drawable.home_default, R.drawable.home),
|
||||
"events" to FooterNavMenuLabel("События", R.drawable.calendar_default, R.drawable.calendar),
|
||||
"notifications" to FooterNavMenuLabel("Уведомления", R.drawable.bell_default, R.drawable.bell),
|
||||
"notifications" to FooterNavMenuLabel(
|
||||
"Уведомления",
|
||||
R.drawable.bell_default,
|
||||
R.drawable.bell
|
||||
),
|
||||
"chat" to FooterNavMenuLabel("Чат", R.drawable.chat_default, R.drawable.chat),
|
||||
"map" to FooterNavMenuLabel("Карта", R.drawable.location_default, R.drawable.location)
|
||||
)
|
||||
|
@ -337,17 +413,23 @@ fun Footer(modifier: Modifier = Modifier,
|
|||
onClick = {
|
||||
activeRoute = it.key
|
||||
hostController.navigate(it.key)
|
||||
},
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = Color.Black,
|
||||
)
|
||||
) {
|
||||
Column (horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if (activeRoute== it.key) {
|
||||
Image(painter = painterResource(id = it.value.imageActive), contentDescription = "")
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if (activeRoute == it.key) {
|
||||
Image(
|
||||
painter = painterResource(id = it.value.imageActive),
|
||||
contentDescription = ""
|
||||
)
|
||||
Text(text = it.value.title, fontSize = 11.sp, color = Color.Black)
|
||||
} else {
|
||||
Image(painter = painterResource(id = it.value.defaultImage), contentDescription = "")
|
||||
Image(
|
||||
painter = painterResource(id = it.value.defaultImage),
|
||||
contentDescription = ""
|
||||
)
|
||||
Text(text = it.value.title, fontSize = 11.sp, color = Color.Gray)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import okhttp3.Response
|
|||
import okhttp3.internal.EMPTY_REQUEST
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.Type
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
data class Langs(
|
||||
|
@ -41,11 +42,10 @@ inline fun <reified T> Response.parseList(): List<T> {
|
|||
|
||||
|
||||
open class Api(val ctx: Context) {
|
||||
val kv = MMKV.defaultMMKV()
|
||||
|
||||
val clientCheckAuth = OkHttpClient.Builder().build()
|
||||
|
||||
val client = OkHttpClient.Builder()
|
||||
val client = OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30 ,TimeUnit.SECONDS)
|
||||
.addInterceptor { chain ->
|
||||
val req = chain.request().newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${Database.access_token}")
|
||||
|
@ -55,7 +55,7 @@ open class Api(val ctx: Context) {
|
|||
.build()
|
||||
|
||||
|
||||
val BASE_URL = "http://147.45.107.119:8000/api/v1"
|
||||
val BASE_URL = "http://192.168.0.101:8000/api/v1"
|
||||
|
||||
fun performRequest(clientReq: Request, errorHandler: (Exception) -> Unit = {}, handler: (Response) -> Unit) {
|
||||
client.newCall(clientReq).enqueue(object : Callback {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.example.mpdriver.api
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract.Data
|
||||
import android.util.Log
|
||||
import com.example.mpdriver.storage.CreateUpdateTaskData
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -9,9 +11,15 @@ import kotlinx.coroutines.GlobalScope
|
|||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import ru.gildor.coroutines.okhttp.await
|
||||
import java.io.IOException
|
||||
import kotlin.math.log
|
||||
|
||||
|
||||
data class SetTaskStatusRequest(
|
||||
|
@ -20,80 +28,110 @@ data class SetTaskStatusRequest(
|
|||
|
||||
data class TaskResponse(
|
||||
|
||||
@SerializedName("id" ) var id: Long? = null,
|
||||
@SerializedName("startPln" ) var startPln: String? = null,
|
||||
@SerializedName("endPln" ) var endPln: String? = null,
|
||||
@SerializedName("startFact" ) var startFact: String? = null,
|
||||
@SerializedName("endFact" ) var endFact: String? = null,
|
||||
@SerializedName("status" ) var status: String? = null,
|
||||
@SerializedName("taskType" ) var taskType: String? = null,
|
||||
@SerializedName("text" ) var text: String? = null,
|
||||
@SerializedName("events" ) var events: ArrayList<Events> = arrayListOf(),
|
||||
@SerializedName("subtasks" ) var subtasks: ArrayList<Subtasks> = arrayListOf(),
|
||||
@SerializedName("route" ) var route: Route? = Route()
|
||||
@SerializedName("id") var id: Long? = null,
|
||||
@SerializedName("startPln") var startPln: String? = null,
|
||||
@SerializedName("endPln") var endPln: String? = null,
|
||||
@SerializedName("startFact") var startFact: String? = null,
|
||||
@SerializedName("endFact") var endFact: String? = null,
|
||||
@SerializedName("status") var status: String? = null,
|
||||
@SerializedName("taskType") var taskType: String? = null,
|
||||
@SerializedName("text") var text: String? = null,
|
||||
@SerializedName("events") var events: ArrayList<Events> = arrayListOf(),
|
||||
@SerializedName("subtasks") var subtasks: ArrayList<Subtasks> = arrayListOf(),
|
||||
@SerializedName("route") var route: Route? = Route()
|
||||
|
||||
)
|
||||
|
||||
data class Events (
|
||||
data class Events(
|
||||
|
||||
@SerializedName("id" ) var id : Long? = null,
|
||||
@SerializedName("type" ) var type : String? = null,
|
||||
@SerializedName("text" ) var text : String? = null,
|
||||
@SerializedName("eventDatetime" ) var eventDatetime : String? = null
|
||||
@SerializedName("id") var id: Long? = null,
|
||||
@SerializedName("type") var type: String? = null,
|
||||
@SerializedName("text") var text: String? = null,
|
||||
@SerializedName("eventDatetime") var eventDatetime: String? = null
|
||||
|
||||
)
|
||||
|
||||
data class Location (
|
||||
data class Location(
|
||||
|
||||
@SerializedName("lat" ) var lat : Double? = null,
|
||||
@SerializedName("lon" ) var lon : Double? = null
|
||||
@SerializedName("lat") var lat: Double? = null,
|
||||
@SerializedName("lon") var lon: Double? = null
|
||||
|
||||
)
|
||||
|
||||
data class Station (
|
||||
data class Station(
|
||||
|
||||
@SerializedName("id" ) var id : Long? = null,
|
||||
@SerializedName("name" ) var name : String? = null,
|
||||
@SerializedName("location" ) var location : Location? = Location()
|
||||
@SerializedName("id") var id: Long? = null,
|
||||
@SerializedName("name") var name: String? = null,
|
||||
@SerializedName("location") var location: Location? = Location()
|
||||
|
||||
)
|
||||
|
||||
data class Subtasks (
|
||||
data class Subtasks(
|
||||
|
||||
@SerializedName("id" ) var id : Long? = null,
|
||||
@SerializedName("parentId" ) var parentId : Long? = null,
|
||||
@SerializedName("startPln" ) var startPln : String? = null,
|
||||
@SerializedName("endPln" ) var endPln : String? = null,
|
||||
@SerializedName("startFact" ) var startFact : String? = null,
|
||||
@SerializedName("endFact" ) var endFact : String? = null,
|
||||
@SerializedName("status" ) var status : String? = null,
|
||||
@SerializedName("taskType" ) var taskType : String? = null,
|
||||
@SerializedName("text" ) var text : String? = null,
|
||||
@SerializedName("station" ) var station : Station? = Station()
|
||||
@SerializedName("id") var id: Long? = null,
|
||||
@SerializedName("parentId") var parentId: Long? = null,
|
||||
@SerializedName("startPln") var startPln: String? = null,
|
||||
@SerializedName("endPln") var endPln: String? = null,
|
||||
@SerializedName("startFact") var startFact: String? = null,
|
||||
@SerializedName("endFact") var endFact: String? = null,
|
||||
@SerializedName("status") var status: String? = null,
|
||||
@SerializedName("taskType") var taskType: String? = null,
|
||||
@SerializedName("text") var text: String? = null,
|
||||
@SerializedName("station") var station: Station? = Station()
|
||||
|
||||
)
|
||||
|
||||
data class TRS (
|
||||
data class TRS(
|
||||
|
||||
@SerializedName("id" ) var id : Long? = null,
|
||||
@SerializedName("gost" ) var gost : String? = null
|
||||
@SerializedName("id") var id: Long? = null,
|
||||
@SerializedName("gost") var gost: String? = null
|
||||
|
||||
)
|
||||
|
||||
data class Route (
|
||||
data class Route(
|
||||
|
||||
@SerializedName("id" ) var id : Long? = null,
|
||||
@SerializedName("temperatureProperty" ) var temperatureProperty : Int? = null,
|
||||
@SerializedName("name" ) var name : String? = null,
|
||||
@SerializedName("trailer" ) var trailer : TRS? = null,
|
||||
@SerializedName("truck" ) var truck : TRS? = null
|
||||
@SerializedName("id") var id: Long? = null,
|
||||
@SerializedName("temperatureProperty") var temperatureProperty: Int? = null,
|
||||
@SerializedName("name") var name: String? = null,
|
||||
@SerializedName("trailer") var trailer: TRS? = null,
|
||||
@SerializedName("truck") var truck: TRS? = null
|
||||
|
||||
)
|
||||
|
||||
|
||||
class TaskApi(ctx: Context): Api(ctx) {
|
||||
class TaskApi(ctx: Context) : Api(ctx) {
|
||||
|
||||
suspend fun getPlannedTasksApiCall(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
||||
data class SendTaskStatusReq(
|
||||
val data: List<CreateUpdateTaskData>
|
||||
)
|
||||
|
||||
fun send_task_status_chains(onResponse: (Response) -> Unit = {}, onFailure: () -> Unit = {}) {
|
||||
val chainBody = SendTaskStatusReq(Database.updateTasks).toJson().toRequestBody()
|
||||
val req = Request.Builder().url("$BASE_URL/tasks").post(chainBody).build()
|
||||
client.newCall(req).enqueue(
|
||||
object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
Log.d("taskAPI", "${e}")
|
||||
onFailure()
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
Log.d("taskAPI", response.body.toString())
|
||||
if (response.isSuccessful) {
|
||||
return onResponse(response)
|
||||
}
|
||||
return onFailure()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getPlannedTasksApiCall(
|
||||
errorHandler: (Exception) -> Unit = {},
|
||||
handler: (List<TaskResponse>) -> Unit
|
||||
) {
|
||||
val req = Request.Builder()
|
||||
.url("$BASE_URL/tasks/planned")
|
||||
.build()
|
||||
|
@ -103,21 +141,24 @@ class TaskApi(ctx: Context): Api(ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getPlannedTasks(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
||||
runBlocking {
|
||||
launch(Dispatchers.IO) {
|
||||
getPlannedTasksApiCall {
|
||||
println(Database.planned_tasks)
|
||||
println(it)
|
||||
Database.planned_tasks = it
|
||||
println(Database.planned_tasks)
|
||||
}
|
||||
}
|
||||
handler(Database.planned_tasks)
|
||||
}
|
||||
}
|
||||
// fun getPlannedTasks(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
||||
// runBlocking {
|
||||
// launch(Dispatchers.IO) {
|
||||
// getPlannedTasksApiCall {
|
||||
// println(Database.planned_tasks)
|
||||
// println(it)
|
||||
// Database.planned_tasks = it
|
||||
// println(Database.planned_tasks)
|
||||
// }
|
||||
// }
|
||||
// handler(Database.planned_tasks)
|
||||
// }
|
||||
// }
|
||||
|
||||
suspend fun getActiveTaskApiCall(errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||
suspend fun getActiveTaskApiCall(
|
||||
errorHandler: (Exception) -> Unit = {},
|
||||
handler: (TaskResponse) -> Unit
|
||||
) {
|
||||
val req = Request.Builder()
|
||||
.url("$BASE_URL/tasks/active")
|
||||
.build()
|
||||
|
@ -126,21 +167,24 @@ class TaskApi(ctx: Context): Api(ctx) {
|
|||
handler(it.parse<TaskResponse>())
|
||||
}
|
||||
}
|
||||
fun getActiveTask(errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||
runBlocking {
|
||||
launch(Dispatchers.IO) {
|
||||
getAllTasksForUser()
|
||||
}
|
||||
// fun getActiveTask(errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||
// runBlocking {
|
||||
// launch(Dispatchers.IO) {
|
||||
// getAllTasksForUser()
|
||||
// }
|
||||
//
|
||||
// val data = Database.tasks.filter { it.status == "InProgress" }
|
||||
// if (data.count() >= 1) {
|
||||
// return@runBlocking handler(data[0])
|
||||
// }
|
||||
// handler(TaskResponse())
|
||||
// }
|
||||
// }
|
||||
|
||||
val data = Database.tasks.filter { it.status == "InProgress" }
|
||||
if (data.count() >= 1) {
|
||||
return@runBlocking handler(data[0])
|
||||
}
|
||||
handler(TaskResponse())
|
||||
}
|
||||
}
|
||||
|
||||
fun getCompletedTask(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
||||
fun getCompletedTask(
|
||||
errorHandler: (Exception) -> Unit = {},
|
||||
handler: (List<TaskResponse>) -> Unit
|
||||
) {
|
||||
val req = Request.Builder()
|
||||
.url("$BASE_URL/tasks/completed")
|
||||
.build()
|
||||
|
@ -149,7 +193,12 @@ class TaskApi(ctx: Context): Api(ctx) {
|
|||
handler(it.parseList<TaskResponse>())
|
||||
}
|
||||
}
|
||||
fun getSubtasks(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (List<Subtasks>) -> Unit) {
|
||||
|
||||
fun getSubtasks(
|
||||
taskId: Long,
|
||||
errorHandler: (Exception) -> Unit = {},
|
||||
handler: (List<Subtasks>) -> Unit
|
||||
) {
|
||||
val req = Request.Builder()
|
||||
.url("$BASE_URL/tasks/$taskId/subtasks")
|
||||
.build()
|
||||
|
@ -158,7 +207,12 @@ class TaskApi(ctx: Context): Api(ctx) {
|
|||
handler(it.parseList<Subtasks>())
|
||||
}
|
||||
}
|
||||
fun getEvents(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (List<Events>) -> Unit) {
|
||||
|
||||
fun getEvents(
|
||||
taskId: Long,
|
||||
errorHandler: (Exception) -> Unit = {},
|
||||
handler: (List<Events>) -> Unit
|
||||
) {
|
||||
val req = Request.Builder()
|
||||
.url("$BASE_URL/tasks/$taskId/events")
|
||||
.build()
|
||||
|
@ -168,24 +222,24 @@ class TaskApi(ctx: Context): Api(ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getAllTasksForUser() {
|
||||
val req = Request.Builder().url("$BASE_URL/tasks").build()
|
||||
performRequest(req) {res ->
|
||||
val data = res.parseList<TaskResponse>()
|
||||
Database.tasks = data
|
||||
}
|
||||
}
|
||||
// fun getAllTasksForUser() {
|
||||
// val req = Request.Builder().url("$BASE_URL/tasks").build()
|
||||
// performRequest(req) {res ->
|
||||
// val data = res.parseList<TaskResponse>()
|
||||
// Database.tasks = data
|
||||
// }
|
||||
// }
|
||||
|
||||
fun setTaskStatusInProgress(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||
|
||||
val body = SetTaskStatusRequest(taskId).toJson().toRequestBody("application/json".toMediaType())
|
||||
|
||||
val req = Request.Builder()
|
||||
.url("$BASE_URL/tasks/active")
|
||||
.post(body)
|
||||
|
||||
performRequest(req.build(), errorHandler) {
|
||||
handler(it.parse())
|
||||
}
|
||||
}
|
||||
// fun setTaskStatusInProgress(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||
//
|
||||
// val body = SetTaskStatusRequest(taskId).toJson().toRequestBody("application/json".toMediaType())
|
||||
//
|
||||
// val req = Request.Builder()
|
||||
// .url("$BASE_URL/tasks/active")
|
||||
// .post(body)
|
||||
//
|
||||
// performRequest(req.build(), errorHandler) {
|
||||
// handler(it.parse())
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.example.mpdriver.api
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.apollographql.apollo3.ApolloClient
|
||||
import com.apollographql.apollo3.network.http.DefaultHttpEngine
|
||||
import com.example.mpdriver.FetchApplicationDataQuery
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.tencent.mmkv.MMKV
|
||||
|
||||
|
||||
val apolloClient = ApolloClient.Builder().serverUrl("http://192.168.0.101:8000/graphql").httpEngine(DefaultHttpEngine(timeoutMillis = 60000)).build()
|
||||
|
||||
|
||||
suspend fun ApolloClient.fetchAppDataToDB(): Unit {
|
||||
val kv = Database.kv
|
||||
|
||||
val req = this.query(FetchApplicationDataQuery("1125904232173609")).execute().data
|
||||
|
||||
req?.let {
|
||||
for (task in req.tasks) {
|
||||
kv.encode("task:${task.id}", task.toJson())
|
||||
}
|
||||
for (subtask in req.subtasks) {
|
||||
kv.encode("subtask:${subtask.id}", subtask.toJson())
|
||||
}
|
||||
for (note in req.notes) {
|
||||
kv.encode("note:${note.id}", note.toJson())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.example.mpdriver.components
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
|
@ -17,6 +18,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Notifications
|
||||
|
@ -39,13 +41,20 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.OffsetMapping
|
||||
import androidx.compose.ui.text.input.TransformedText
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -54,9 +63,17 @@ import androidx.navigation.NavHostController
|
|||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.apollographql.apollo3.api.ApolloResponse
|
||||
import com.apollographql.apollo3.api.toInput
|
||||
import com.example.mpdriver.GetSubtaskByIDQuery
|
||||
import com.example.mpdriver.GetTaskByIdQuery
|
||||
import com.example.mpdriver.R
|
||||
import com.example.mpdriver.api.Subtasks
|
||||
import com.example.mpdriver.api.apolloClient
|
||||
import com.example.mpdriver.recievers.TimeTickReciever
|
||||
import com.example.mpdriver.storage.CreateUpdateTaskData
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.example.mpdriver.type.StatusEnumQl
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -64,10 +81,13 @@ import kotlinx.coroutines.runBlocking
|
|||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.DateTimeUnit
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.UtcOffset
|
||||
import kotlinx.datetime.asTimeZone
|
||||
import kotlinx.datetime.format.byUnicodePattern
|
||||
import kotlinx.datetime.format.char
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import kotlinx.datetime.until
|
||||
import kotlin.math.abs
|
||||
|
||||
|
@ -76,7 +96,7 @@ import kotlin.math.abs
|
|||
fun Subtask(
|
||||
modifier: Modifier = Modifier,
|
||||
children: @Composable () -> Unit = {},
|
||||
subtask: Subtasks = Subtasks(1, startPln = "2024-06-01T00:00", endPln = "2024-06-10T00:00"),
|
||||
subtaskID: Long = 0,
|
||||
footerButton: @Composable () -> Unit = {}
|
||||
) {
|
||||
|
||||
|
@ -91,13 +111,39 @@ fun Subtask(
|
|||
var nowTime by remember {
|
||||
mutableStateOf(Clock.System.now())
|
||||
}
|
||||
var subtaskResponse by remember {
|
||||
mutableStateOf<GetSubtaskByIDQuery.Subtask?>(null)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
||||
val sbtDB = Database.subtasks.find { it.id == subtaskID.toString() }
|
||||
|
||||
sbtDB?.let {
|
||||
subtaskResponse = GetSubtaskByIDQuery.Subtask(
|
||||
sbtDB.id,
|
||||
sbtDB.text,
|
||||
startPln = sbtDB.startPln,
|
||||
endPln = sbtDB.endPln,
|
||||
status = sbtDB.status
|
||||
)
|
||||
|
||||
}
|
||||
// subtaskResponse = apolloClient.query(GetSubtaskByIDQuery("1125904232173609", subtaskID.toString())).execute()
|
||||
}
|
||||
|
||||
TimeTickReciever.registerHandler {
|
||||
nowTime = Clock.System.now()
|
||||
}
|
||||
|
||||
val startPln = LocalDateTime.parse(subtask.startPln!!)
|
||||
val endPln = LocalDateTime.parse(subtask.endPln!!)
|
||||
var startPln: LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.UTC)
|
||||
var endPln: LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.UTC)
|
||||
|
||||
subtaskResponse?.let {
|
||||
startPln = LocalDateTime.parse(subtaskResponse?.startPln.toString())
|
||||
endPln = LocalDateTime.parse(subtaskResponse?.endPln.toString())
|
||||
}
|
||||
|
||||
|
||||
val dateFormat = LocalDateTime.Format {
|
||||
byUnicodePattern("d.MM.yyyy")
|
||||
|
@ -119,10 +165,10 @@ fun Subtask(
|
|||
|
||||
|
||||
val status = when {
|
||||
subtask.status == "Completed" && !isDelay -> TaskStatus.SUCCESS
|
||||
subtask.status == "Completed" && isDelay -> TaskStatus.WARNING
|
||||
subtask.status == "Cancelled" -> TaskStatus.WARNING
|
||||
subtask.status == "InProgress" || subtask.status == "NotDefined" && isDelay -> TaskStatus.DANGER
|
||||
subtaskResponse?.status == StatusEnumQl.COMPLETED && !isDelay -> TaskStatus.SUCCESS
|
||||
subtaskResponse?.status == StatusEnumQl.COMPLETED && isDelay -> TaskStatus.WARNING
|
||||
subtaskResponse?.status == StatusEnumQl.CANCELLED -> TaskStatus.WARNING
|
||||
(subtaskResponse?.status == StatusEnumQl.IN_PROGRESS || subtaskResponse?.status == StatusEnumQl.NOT_DEFINED) && isDelay -> TaskStatus.DANGER
|
||||
else -> TaskStatus.DEFAULT
|
||||
}
|
||||
|
||||
|
@ -151,7 +197,7 @@ fun Subtask(
|
|||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "${subtask.text}",
|
||||
text = "${subtaskResponse?.text}",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
|
@ -259,8 +305,8 @@ fun Subtask(
|
|||
}
|
||||
}
|
||||
|
||||
if (isActionVisible) {
|
||||
IsSubtaskCompletedAction(setStateAction = { isActionVisible = false }, subtask)
|
||||
if (isActionVisible && subtaskResponse != null) {
|
||||
IsSubtaskCompletedAction(setStateAction = { isActionVisible = false }, subtaskResponse!!)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -268,7 +314,10 @@ fun Subtask(
|
|||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun IsSubtaskCompletedAction(setStateAction: (Boolean) -> Unit = { }, subtask: Subtasks) {
|
||||
fun IsSubtaskCompletedAction(
|
||||
setStateAction: (Boolean) -> Unit = { },
|
||||
subtask: GetSubtaskByIDQuery.Subtask
|
||||
) {
|
||||
var currentStep by remember {
|
||||
mutableStateOf(0)
|
||||
}
|
||||
|
@ -318,6 +367,10 @@ fun IsSubtaskCompletedAction(setStateAction: (Boolean) -> Unit = { }, subtask: S
|
|||
composable("success") {
|
||||
SuccessStep(subtask = subtask, controller = actionController)
|
||||
title = "Когда вы выполнили подзадачу?"
|
||||
isFullScreen = true
|
||||
LaunchedEffect(sheetState) {
|
||||
sheetState.expand()
|
||||
}
|
||||
}
|
||||
composable("failure") {
|
||||
isFullScreen = true
|
||||
|
@ -339,13 +392,13 @@ fun IsSubtaskCompletedAction(setStateAction: (Boolean) -> Unit = { }, subtask: S
|
|||
|
||||
|
||||
@Composable
|
||||
fun InitialStep(subtask: Subtasks, controller: NavHostController) {
|
||||
fun InitialStep(subtask: GetSubtaskByIDQuery.Subtask, controller: NavHostController) {
|
||||
Column {
|
||||
JDEButton(type = ButtonType.SUCCESS, onClick = { controller.navigate("success") }) {
|
||||
Text(text = "Подзадача выполнена", fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
JDEButton(type = ButtonType.WARNING, onClick = {controller.navigate("failure")}) {
|
||||
JDEButton(type = ButtonType.WARNING, onClick = { controller.navigate("failure") }) {
|
||||
Text(text = "У меня возникла проблема", fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
@ -354,10 +407,9 @@ fun InitialStep(subtask: Subtasks, controller: NavHostController) {
|
|||
}
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun SuccessStep(
|
||||
subtask: Subtasks = Subtasks(),
|
||||
subtask: GetSubtaskByIDQuery.Subtask,
|
||||
controller: NavHostController = rememberNavController()
|
||||
) {
|
||||
|
||||
|
@ -402,26 +454,96 @@ fun SuccessStep(
|
|||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
TextInput(modifier = Modifier.weight(.66f), date, { date = it }, "Дата")
|
||||
TextInput(modifier = Modifier.weight(.66f), date, {
|
||||
date = it.filter { it.isDigit() }
|
||||
}, "Дата")
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
TextInput(modifier = Modifier.weight(.33f), time, { date = time }, "Время")
|
||||
TextInput(
|
||||
modifier = Modifier.weight(.33f),
|
||||
time,
|
||||
{ time = it.filter { it.isDigit() } },
|
||||
"Время"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
ActiveButton(onClick = { /*TODO*/ }, text = "Сохранить", modifier = Modifier.fillMaxWidth())
|
||||
ActiveButton(onClick = {
|
||||
val task = Database.tasks.find {
|
||||
it.subtasks.find { sbt ->
|
||||
sbt.id == subtask.id
|
||||
} != null
|
||||
}
|
||||
|
||||
val dt = LocalDateTime.parse(date + "T" + time, LocalDateTime.Format {
|
||||
dayOfMonth()
|
||||
monthNumber()
|
||||
year()
|
||||
char('T')
|
||||
hour()
|
||||
minute()
|
||||
}).toInstant(TimeZone.UTC).toEpochMilliseconds()
|
||||
|
||||
task?.let {
|
||||
Database.createUpdateTaskDataLocally(
|
||||
CreateUpdateTaskData(
|
||||
subtask.id.toLong(),
|
||||
dt = dt,
|
||||
status = "Completed"
|
||||
)
|
||||
)
|
||||
|
||||
if (subtask.id == task.subtasks.last().id) {
|
||||
Database.createUpdateTaskDataLocally(
|
||||
CreateUpdateTaskData(
|
||||
task.id.toLong(),
|
||||
dt = dt,
|
||||
status = "Completed"
|
||||
)
|
||||
)
|
||||
return@let
|
||||
}
|
||||
val curr_sbt_idx = task.subtasks.indexOf(task.subtasks.find { it.id == subtask.id })
|
||||
|
||||
Database.createUpdateTaskDataLocally(
|
||||
CreateUpdateTaskData(
|
||||
task.subtasks[curr_sbt_idx + 1].id.toLong(),
|
||||
dt,
|
||||
status = "InProgress"
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}, text = "Сохранить", modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun transformText(it: String): String {
|
||||
var digits = it.filter { it.isDigit() }
|
||||
if (digits.length > 8) {
|
||||
digits = digits.slice(0..<8)
|
||||
}
|
||||
|
||||
var data = when (digits.length) {
|
||||
in 0..2 -> "${digits}"
|
||||
in 3..4 -> "${digits.substring(0..<2)}.${digits.substring(2)}"
|
||||
in 5..8 -> "${digits.substring(0..<2)}.${digits.substring(2..<4)}.${digits.substring(4)}"
|
||||
else -> digits
|
||||
}
|
||||
println(data)
|
||||
return data
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun FailureStep(
|
||||
subtask: Subtasks = Subtasks(),
|
||||
subtask: GetSubtaskByIDQuery.Subtask,
|
||||
controller: NavHostController = rememberNavController()
|
||||
) {
|
||||
|
||||
|
@ -440,7 +562,8 @@ fun FailureStep(
|
|||
color = Color.Gray
|
||||
)
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
TextField(value = failureDesk,
|
||||
TextField(
|
||||
value = failureDesk,
|
||||
onValueChange = { failureDesk = it },
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
@ -466,10 +589,12 @@ fun FailureStep(
|
|||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent
|
||||
),
|
||||
trailingIcon = {Icon(
|
||||
painter = painterResource(id = R.drawable.calendar_default),
|
||||
contentDescription = ""
|
||||
)}
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.calendar_default),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
|
@ -485,15 +610,19 @@ fun FailureStep(
|
|||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent
|
||||
),
|
||||
trailingIcon = {Icon(painter = painterResource(id = R.drawable.calendar_default),
|
||||
contentDescription = ""
|
||||
)}
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.calendar_default),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
ActiveButton(onClick = { /*TODO*/ }, text = "Отправить", modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun TextInput(
|
||||
|
@ -507,6 +636,8 @@ fun TextInput(
|
|||
mutableStateOf(true)
|
||||
}
|
||||
|
||||
val kbController = LocalSoftwareKeyboardController.current
|
||||
|
||||
|
||||
BasicTextField(
|
||||
modifier = modifier
|
||||
|
@ -515,7 +646,13 @@ fun TextInput(
|
|||
},
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Number,
|
||||
imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onDone = {
|
||||
kbController?.hide()
|
||||
}),
|
||||
decorationBox = {
|
||||
Box(
|
||||
Modifier
|
||||
|
@ -525,16 +662,16 @@ fun TextInput(
|
|||
.padding(horizontal = 10.dp, vertical = 5.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(if (isFocused) Alignment.TopStart else Alignment.CenterStart),
|
||||
modifier = Modifier.align(if (isFocused || value != "") Alignment.TopStart else Alignment.CenterStart),
|
||||
text = placeholder,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = if (isFocused) 13.sp else 18.sp,
|
||||
fontSize = if (isFocused || value != "") 13.sp else 18.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
if (isFocused) {
|
||||
if (isFocused || value != "") {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.BottomStart),
|
||||
text = value,
|
||||
text = transformText(value),
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
|
|
@ -14,12 +14,14 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.KeyboardArrowUp
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
@ -32,10 +34,13 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.mpdriver.GetTaskByIdQuery
|
||||
import com.example.mpdriver.R
|
||||
import com.example.mpdriver.api.Subtasks
|
||||
import com.example.mpdriver.api.TaskResponse
|
||||
import com.example.mpdriver.api.apolloClient
|
||||
import com.example.mpdriver.recievers.TimeTickReciever
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.example.mpdriver.type.MarshTemperaturePropertyQL
|
||||
import com.example.mpdriver.type.StatusEnumQl
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.DateTimeUnit
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
@ -46,7 +51,6 @@ import kotlinx.datetime.toInstant
|
|||
import kotlinx.datetime.until
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
enum class TaskStatus {
|
||||
DANGER,
|
||||
SUCCESS,
|
||||
|
@ -58,8 +62,47 @@ enum class TaskStatus {
|
|||
@Composable
|
||||
fun Task(modifier: Modifier = Modifier,
|
||||
children: @Composable() ()->Unit = {},
|
||||
taskResponse: TaskResponse = TaskResponse(1, "2024-06-01T00:00", "2024-06-10T00:00"),
|
||||
task_id: Long = 1,
|
||||
footerButton: @Composable ()-> Unit = {}) {
|
||||
var taskResponse by remember {
|
||||
mutableStateOf<GetTaskByIdQuery.Data?>(null)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
|
||||
val dbTask = Database.tasks.find { it.id == task_id.toString() }
|
||||
var task: GetTaskByIdQuery.Task
|
||||
|
||||
dbTask?.let {
|
||||
|
||||
val trailer = when(dbTask.route.trailer) {
|
||||
null -> null
|
||||
else -> GetTaskByIdQuery.Trailer(gost = dbTask.route.trailer.gost)
|
||||
}
|
||||
val truck = when(dbTask.route.truck) {
|
||||
null -> null
|
||||
else -> GetTaskByIdQuery.Truck(gost = dbTask.route.truck.gost)
|
||||
}
|
||||
|
||||
val route = GetTaskByIdQuery.Route(name = dbTask.route.name, dbTask.route.temperatureProperty, truck, trailer)
|
||||
task = GetTaskByIdQuery.Task(status = dbTask.status, route, text = dbTask.text, startPln = dbTask.startPln, endPln = dbTask.endPln, startFact = dbTask.startFact, endFact = dbTask.endFact)
|
||||
taskResponse = GetTaskByIdQuery.Data(task)
|
||||
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
taskResponse = apolloClient.query(GetTaskByIdQuery(task_id.toString(), "1125904232173609")).execute().data
|
||||
|
||||
}
|
||||
|
||||
val dateFormat = LocalDateTime.Format {
|
||||
byUnicodePattern("d.MM.yyyy")
|
||||
}
|
||||
|
||||
val timeFormat = LocalDateTime.Format {
|
||||
byUnicodePattern("HH:mm")
|
||||
}
|
||||
|
||||
|
||||
var expanded by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
@ -72,205 +115,208 @@ fun Task(modifier: Modifier = Modifier,
|
|||
nowTime = Clock.System.now()
|
||||
}
|
||||
|
||||
val startPln = LocalDateTime.parse(taskResponse.startPln!!)
|
||||
val endPln = LocalDateTime.parse(taskResponse.endPln!!)
|
||||
taskResponse?.let {
|
||||
val startPln = LocalDateTime.parse(it.task!!.startPln.toString())
|
||||
val endPln = LocalDateTime.parse(it.task.endPln.toString())
|
||||
val leastBase = nowTime.until(
|
||||
endPln.toInstant(offset = UtcOffset(3)),
|
||||
timeZone = UtcOffset(3).asTimeZone(), unit = DateTimeUnit.MINUTE)
|
||||
|
||||
val dateFormat = LocalDateTime.Format {
|
||||
byUnicodePattern("d.MM.yyyy")
|
||||
}
|
||||
|
||||
val timeFormat = LocalDateTime.Format {
|
||||
byUnicodePattern("HH:mm")
|
||||
}
|
||||
|
||||
val leastBase = nowTime.until(
|
||||
endPln.toInstant(offset = UtcOffset(3)),
|
||||
timeZone = UtcOffset(3).asTimeZone(), unit = DateTimeUnit.MINUTE)
|
||||
|
||||
val isDelay = leastBase != abs(leastBase)
|
||||
val leastDays = abs( leastBase) / 60 / 24
|
||||
val leastHours = abs(leastBase) / 60 - leastDays * 24
|
||||
val leastMinutes = abs(leastBase) - leastDays * 60 * 24 - leastHours * 60
|
||||
val isDelay = leastBase != abs(leastBase)
|
||||
val leastDays = abs( leastBase) / 60 / 24
|
||||
val leastHours = abs(leastBase) / 60 - leastDays * 24
|
||||
val leastMinutes = abs(leastBase) - leastDays * 60 * 24 - leastHours * 60
|
||||
|
||||
|
||||
val status = when {
|
||||
taskResponse.status == "Completed" && !isDelay -> TaskStatus.SUCCESS
|
||||
taskResponse.status == "Completed" && isDelay -> TaskStatus.WARNING
|
||||
taskResponse.status == "Cancelled" -> TaskStatus.WARNING
|
||||
taskResponse.status == "InProgress" || taskResponse.status == "NotDefined" && isDelay -> TaskStatus.DANGER
|
||||
else -> TaskStatus.DEFAULT
|
||||
}
|
||||
val status = when {
|
||||
it.task.status == StatusEnumQl.COMPLETED && !isDelay -> TaskStatus.SUCCESS
|
||||
it.task.status == StatusEnumQl.COMPLETED && isDelay -> TaskStatus.WARNING
|
||||
it.task.status == StatusEnumQl.CANCELLED -> TaskStatus.WARNING
|
||||
(it.task.status == StatusEnumQl.IN_PROGRESS || it.task.status == StatusEnumQl.NOT_DEFINED) && isDelay -> TaskStatus.DANGER
|
||||
else -> TaskStatus.DEFAULT
|
||||
}
|
||||
|
||||
|
||||
|
||||
Column(
|
||||
modifier
|
||||
.border(
|
||||
2.dp, when (status) {
|
||||
TaskStatus.DEFAULT -> Color.Gray
|
||||
TaskStatus.SUCCESS -> Color(0xFF45900B)
|
||||
TaskStatus.DANGER -> Color(0xFFE5332A)
|
||||
TaskStatus.WARNING -> Color(0xFFFFC700)
|
||||
}, RoundedCornerShape(10.dp)
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 15.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "Движение по маршруту\n${taskResponse.route?.name}",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
||||
IconButton(onClick = { /*TODO*/ }, modifier = Modifier.weight(0.1f)) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.tick_default),
|
||||
contentDescription = "tick"
|
||||
return Column(
|
||||
modifier
|
||||
.border(
|
||||
2.dp, when (status) {
|
||||
TaskStatus.DEFAULT -> Color.Gray
|
||||
TaskStatus.SUCCESS -> Color(0xFF45900B)
|
||||
TaskStatus.DANGER -> Color(0xFFE5332A)
|
||||
TaskStatus.WARNING -> Color(0xFFFFC700)
|
||||
}, RoundedCornerShape(10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = "${taskResponse.route?.truck?.gost}",
|
||||
subText = "Номер ТС"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = "${if (taskResponse.route!!.trailer != null) taskResponse.route!!.trailer!!.gost else "-"}",
|
||||
subText = "Номер прицепа"
|
||||
)
|
||||
}
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 20.dp),
|
||||
mainText = if (taskResponse.route!!.temperatureProperty == 1) "Горячая" else "Холодная",
|
||||
subText = "Тип перевозки"
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
) {
|
||||
when (taskResponse.route!!.temperatureProperty) {
|
||||
1 -> Image(painter = painterResource(id = R.drawable.hottransport), contentDescription = "" )
|
||||
else -> Image(painter = painterResource(id = R.drawable.coltransport), contentDescription = "" )
|
||||
}
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 15.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "Движение по маршруту\n${it.task.route.name}",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
||||
}
|
||||
children()
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
Column {
|
||||
IconButton(onClick = { /*TODO*/ }, modifier = Modifier.weight(0.1f)) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.tick_default),
|
||||
contentDescription = "tick"
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 20.dp),
|
||||
mainText = "${taskResponse.text}",
|
||||
subText = "Полный маршрут"
|
||||
Modifier.weight(1f),
|
||||
mainText = "${if (it.task.route.truck != null) it.task.route.truck.gost else "-"}",
|
||||
subText = "Номер ТС"
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
text = "Начало маршрута",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = "${if (it.task.route.trailer != null) it.task.route.trailer.gost else "-"}",
|
||||
subText = "Номер прицепа"
|
||||
)
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(2f),
|
||||
mainText = dateFormat.format(startPln),
|
||||
subText = "Дата"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = timeFormat.format(startPln),
|
||||
subText = "Время"
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
text = "Конец маршрута",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(2f),
|
||||
mainText = dateFormat.format(endPln),
|
||||
subText = "Дата"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = timeFormat.format(endPln),
|
||||
subText = "Время"
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
text = if (isDelay) "Задержка" else "Осталось времени до конца маршрута",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = if (isDelay) 20.sp else 14.sp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Black
|
||||
)
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.border(
|
||||
1.dp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
),
|
||||
mainText = leastDays.toString(),
|
||||
subText = "Дни"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.border(
|
||||
1.dp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
),
|
||||
mainText = leastHours.toString(),
|
||||
subText = "Часы"
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.border(
|
||||
1.dp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
),
|
||||
mainText = leastMinutes.toString(),
|
||||
subText = "Минуты"
|
||||
)
|
||||
}
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 20.dp),
|
||||
mainText = if (it.task.route.temperatureProperty == MarshTemperaturePropertyQL.HOT) "Горячая" else "Холодная",
|
||||
subText = "Тип перевозки"
|
||||
) {
|
||||
when (it.task.route.temperatureProperty) {
|
||||
MarshTemperaturePropertyQL.HOT -> Image(painter = painterResource(id = R.drawable.hottransport), contentDescription = "" )
|
||||
else -> Image(painter = painterResource(id = R.drawable.coltransport), contentDescription = "" )
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Divider(thickness = 2.dp, color = Color.Gray)
|
||||
TextButton(onClick = { expanded = !expanded }, colors = ButtonDefaults.buttonColors(contentColor = Color.Black, containerColor = Color.Transparent)) {
|
||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
Text(text = if (expanded) "Скрыть" else "Развернуть", fontSize = 16.sp)
|
||||
Icon(Icons.Rounded.KeyboardArrowUp, contentDescription = "Chevron")
|
||||
children()
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
Column {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 20.dp),
|
||||
mainText = it.task.text,
|
||||
subText = "Полный маршрут"
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
text = "Начало маршрута",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(2f),
|
||||
mainText = dateFormat.format(startPln),
|
||||
subText = "Дата"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = timeFormat.format(startPln),
|
||||
subText = "Время"
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
text = "Конец маршрута",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(2f),
|
||||
mainText = dateFormat.format(endPln),
|
||||
subText = "Дата"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier.weight(1f),
|
||||
mainText = timeFormat.format(endPln),
|
||||
subText = "Время"
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
text = if (isDelay) "Задержка" else "Осталось времени до конца маршрута",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = if (isDelay) 20.sp else 14.sp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Black
|
||||
)
|
||||
Row(Modifier.padding(bottom = 20.dp)) {
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.border(
|
||||
1.dp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
),
|
||||
mainText = leastDays.toString(),
|
||||
subText = "Дни"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.border(
|
||||
1.dp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
),
|
||||
mainText = leastHours.toString(),
|
||||
subText = "Часы"
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
InformationPlaceholderSmall(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.border(
|
||||
1.dp,
|
||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
),
|
||||
mainText = leastMinutes.toString(),
|
||||
subText = "Минуты"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Divider(thickness = 2.dp, color = Color.Gray)
|
||||
TextButton(onClick = { expanded = !expanded }, colors = ButtonDefaults.buttonColors(contentColor = Color.Black, containerColor = Color.Transparent)) {
|
||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
Text(text = if (expanded) "Скрыть" else "Развернуть", fontSize = 16.sp)
|
||||
Icon(Icons.Rounded.KeyboardArrowUp, contentDescription = "Chevron")
|
||||
}
|
||||
}
|
||||
footerButton()
|
||||
|
||||
|
||||
|
||||
}
|
||||
footerButton()
|
||||
}
|
||||
|
||||
return Column (
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
@ -39,24 +40,44 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.apollographql.apollo3.api.ApolloResponse
|
||||
import com.example.mpdriver.GetActiveSubtaskIDQuery
|
||||
import com.example.mpdriver.GetActiveTaskIdQuery
|
||||
import com.example.mpdriver.GetTaskByIdQuery
|
||||
import com.example.mpdriver.api.TaskResponse
|
||||
import com.example.mpdriver.api.apolloClient
|
||||
import com.example.mpdriver.components.ButtonType
|
||||
import com.example.mpdriver.components.EmptyList
|
||||
import com.example.mpdriver.components.IteractionButton
|
||||
import com.example.mpdriver.components.JDEButton
|
||||
import com.example.mpdriver.components.Subtask
|
||||
import com.example.mpdriver.components.Task
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.example.mpdriver.type.StatusEnumQl
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ActiveTask(activeTask: TaskResponse? = null, hostController: NavHostController) {
|
||||
fun ActiveTask(activeTaskID: Long? = null, hostController: NavHostController) {
|
||||
|
||||
var responseGetActiveSubtaskID by remember {
|
||||
mutableStateOf<GetActiveSubtaskIDQuery.ActiveSubtask?>(null)
|
||||
}
|
||||
|
||||
activeTaskID?.let {
|
||||
LaunchedEffect(Unit) {
|
||||
val sbtDB = Database.subtasks.find { it.status == StatusEnumQl.IN_PROGRESS }
|
||||
sbtDB?.let {
|
||||
responseGetActiveSubtaskID = GetActiveSubtaskIDQuery.ActiveSubtask(sbtDB.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 15.dp)
|
||||
) {
|
||||
if (activeTask == null) {
|
||||
if (activeTaskID == null) {
|
||||
EmptyList(Modifier.padding(vertical = 60.dp), text = "У вас нет активных задач")
|
||||
return
|
||||
}
|
||||
|
@ -66,9 +87,9 @@ fun ActiveTask(activeTask: TaskResponse? = null, hostController: NavHostControll
|
|||
fontSize = 18.sp
|
||||
)
|
||||
|
||||
activeTask.let {
|
||||
IteractionButton(onClick = { hostController.navigate("tasks/${it.id}") }) {
|
||||
Task(taskResponse = it)
|
||||
activeTaskID.let {
|
||||
IteractionButton(onClick = { hostController.navigate("tasks/${activeTaskID}") }) {
|
||||
Task(task_id = activeTaskID)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,14 +102,14 @@ fun ActiveTask(activeTask: TaskResponse? = null, hostController: NavHostControll
|
|||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
|
||||
activeTask.let { task ->
|
||||
responseGetActiveSubtaskID.let { task ->
|
||||
IteractionButton(onClick = { /*TODO*/ }) {
|
||||
Subtask(subtask = task.subtasks.filter { it.status == "InProgress" }[0])
|
||||
responseGetActiveSubtaskID?.let {
|
||||
Subtask(subtaskID = it.id.toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package com.example.mpdriver.components.note
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.mpdriver.components.ActiveButton
|
||||
import com.example.mpdriver.components.JDEButton
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun NoteComponent(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.border(2.dp, color = Color.Gray, shape = RoundedCornerShape(10.dp))
|
||||
.padding(15.dp)) {
|
||||
Text(text = "Изменился статус события", fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
||||
Text(text = "Изменился статус события\n“Прибыть на ПГР 20.12.2023 к 10:00”", fontSize = 15.sp, fontWeight = FontWeight.Normal, color = Color.Gray)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
ActiveButton(onClick = { /*TODO*/ }, text = "Перейти к задаче", modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.example.mpdriver.screens
|
||||
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
@ -19,11 +21,17 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.apollographql.apollo3.api.ApolloResponse
|
||||
import com.example.mpdriver.GetActiveTaskIdQuery
|
||||
import com.example.mpdriver.GetTaskByIdQuery
|
||||
import com.example.mpdriver.api.TaskApi
|
||||
import com.example.mpdriver.api.TaskResponse
|
||||
import com.example.mpdriver.api.apolloClient
|
||||
import com.example.mpdriver.components.Layout
|
||||
import com.example.mpdriver.components.feed.ActiveTask
|
||||
import com.example.mpdriver.components.feed.FeedTaskDataCard
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.example.mpdriver.type.StatusEnumQl
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.format.byUnicodePattern
|
||||
|
||||
|
@ -37,29 +45,17 @@ fun Feed(modifier: Modifier = Modifier, hostController: NavHostController) {
|
|||
mutableStateOf(true)
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
var activeTask by remember {
|
||||
mutableStateOf<TaskResponse?>(null)
|
||||
mutableStateOf<GetActiveTaskIdQuery.Task?>(null)
|
||||
}
|
||||
var plannedTasks by remember {
|
||||
mutableStateOf<List<TaskResponse>>(emptyList())
|
||||
mutableStateOf(emptyList<GetActiveTaskIdQuery.PlannedTask>())
|
||||
}
|
||||
var completedTasks by remember {
|
||||
mutableStateOf<List<TaskResponse>>(emptyList())
|
||||
mutableStateOf(emptyList<GetActiveTaskIdQuery.CompletedTask>())
|
||||
}
|
||||
|
||||
val taskApi = TaskApi(context)
|
||||
|
||||
taskApi.getActiveTask { task ->
|
||||
isLoading = false
|
||||
task.id?.let {
|
||||
activeTask = task
|
||||
}
|
||||
}
|
||||
|
||||
taskApi.getPlannedTasks { tasks ->
|
||||
plannedTasks = tasks
|
||||
}
|
||||
val dateFormat = LocalDateTime.Format {
|
||||
byUnicodePattern("dd.MM.yyyy")
|
||||
}
|
||||
|
@ -70,7 +66,7 @@ fun Feed(modifier: Modifier = Modifier, hostController: NavHostController) {
|
|||
"count" to plannedTasks.count(),
|
||||
"date" to when (plannedTasks.count()) {
|
||||
0 -> "-"
|
||||
else -> dateFormat.format(LocalDateTime.parse(plannedTasks[0].startPln!!))
|
||||
else -> dateFormat.format(LocalDateTime.parse(plannedTasks[0].startPln.toString()))
|
||||
},
|
||||
"buttonLabel" to "Смотреть запланированные задачи",
|
||||
"dateDescription" to "Ближайшая",
|
||||
|
@ -80,23 +76,59 @@ fun Feed(modifier: Modifier = Modifier, hostController: NavHostController) {
|
|||
"count" to completedTasks.count(),
|
||||
"date" to when (completedTasks.count()) {
|
||||
0 -> "-"
|
||||
else -> dateFormat.format(LocalDateTime.parse(completedTasks[0].startPln!!))
|
||||
else -> dateFormat.format(LocalDateTime.parse(completedTasks[0].startPln.toString()))
|
||||
},
|
||||
"buttonLabel" to "Смотреть завершенные задачи",
|
||||
"dateDescription" to "Последняя",
|
||||
),
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
||||
val tasks = Database.tasks
|
||||
|
||||
val activeTaskDB = tasks.find { it.status == StatusEnumQl.IN_PROGRESS }
|
||||
val completedTasksDB = tasks.filter { it.status == StatusEnumQl.COMPLETED }
|
||||
val plannedTasksDB = tasks.filter { it.status == StatusEnumQl.NOT_DEFINED }
|
||||
|
||||
activeTask = when (activeTaskDB) {
|
||||
null -> null
|
||||
else -> GetActiveTaskIdQuery.Task(id = activeTaskDB.id)
|
||||
}
|
||||
completedTasks = when (completedTasksDB.count()) {
|
||||
0 -> emptyList()
|
||||
else -> completedTasksDB.map {t ->
|
||||
GetActiveTaskIdQuery.CompletedTask(t.id, t.startPln)
|
||||
}
|
||||
}
|
||||
|
||||
plannedTasks = when (plannedTasksDB.count()) {
|
||||
0 -> emptyList()
|
||||
else -> plannedTasksDB.map { t ->
|
||||
GetActiveTaskIdQuery.PlannedTask(t.id, t.startPln)
|
||||
}
|
||||
}
|
||||
Log.d("FEED:PlannedTasks", "${plannedTasks}")
|
||||
Log.d("FEED:ActiveTask", "${activeTask}")
|
||||
Log.d("FEED:CompletedTask", "${completedTasks}")
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
if(isLoading) {
|
||||
Column (Modifier.fillMaxWidth().padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Column (
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Layout(dataList = dataList, header = {
|
||||
Column {
|
||||
ActiveTask(activeTask = activeTask, hostController = hostController)
|
||||
ActiveTask(activeTaskID = activeTask?.id?.toLong(), hostController = hostController)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package com.example.mpdriver.screens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.example.mpdriver.components.Layout
|
||||
import com.example.mpdriver.components.note.NoteComponent
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun NoteScreen() {
|
||||
|
||||
|
||||
Layout(dataList = (0..5).toMutableList()) {
|
||||
NoteComponent()
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import androidx.compose.material3.ExtendedFloatingActionButton
|
|||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -40,6 +41,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.example.mpdriver.FetchApplicationDataQuery
|
||||
import com.example.mpdriver.api.Subtasks
|
||||
import com.example.mpdriver.api.TaskApi
|
||||
import com.example.mpdriver.components.ComposableLifecycle
|
||||
|
@ -47,6 +49,8 @@ import com.example.mpdriver.components.HeaderTabs
|
|||
import com.example.mpdriver.components.HeaderTabsData
|
||||
import com.example.mpdriver.components.Layout
|
||||
import com.example.mpdriver.components.Subtask
|
||||
import com.example.mpdriver.storage.Database
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
|
@ -65,8 +69,9 @@ fun SubtaskScreen(taskId: Long = 0) {
|
|||
|
||||
|
||||
var datalist by remember {
|
||||
mutableStateOf<List<Subtasks>>(mutableListOf())
|
||||
mutableStateOf(emptyList<FetchApplicationDataQuery.Subtask1>())
|
||||
}
|
||||
|
||||
var activeTab by remember {
|
||||
mutableStateOf(0)
|
||||
}
|
||||
|
@ -74,25 +79,24 @@ fun SubtaskScreen(taskId: Long = 0) {
|
|||
val listState = rememberLazyListState()
|
||||
var offset = listState.firstVisibleItemScrollOffset + listState.firstVisibleItemIndex
|
||||
|
||||
val tabsData = listOf<HeaderTabsData>(HeaderTabsData(0, "Подзадачи"), HeaderTabsData(1, "События"))
|
||||
val tabsData =
|
||||
listOf<HeaderTabsData>(HeaderTabsData(0, "Подзадачи"), HeaderTabsData(1, "События"))
|
||||
|
||||
ComposableLifecycle { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_CREATE -> {
|
||||
api.getSubtasks(taskId) {
|
||||
datalist = it
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
val task = Database.tasks.find { it.id == taskId.toString() }
|
||||
datalist = Database.subtasks.filter { sbt ->
|
||||
task!!.subtasks.find { it.id == sbt.id } != null
|
||||
}.sortedBy { LocalDateTime.parse(it.startPln.toString()) }
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
if(isLoading) {
|
||||
Column (
|
||||
|
||||
if (isLoading) {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||
}
|
||||
return
|
||||
|
@ -100,18 +104,26 @@ fun SubtaskScreen(taskId: Long = 0) {
|
|||
|
||||
Layout(state = listState, dataList = datalist, header = {
|
||||
HeaderTabs(modifier = Modifier.onGloballyPositioned { lc ->
|
||||
val cords = lc.boundsInParent()
|
||||
val cords = lc.boundsInParent()
|
||||
floatingActionShowOffset = cords.bottom
|
||||
}, tabsData = tabsData, activeTab = activeTab) {
|
||||
activeTab = it
|
||||
activeTab = it
|
||||
}
|
||||
}) {
|
||||
Subtask(subtask = it)
|
||||
Subtask(subtaskID = it.id!!.toLong())
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = offset + 200 > floatingActionShowOffset + 100, enter = slideInVertically() + fadeIn(), exit = slideOutVertically() + fadeOut()) {
|
||||
ExtendedFloatingActionButton(onClick = { /*TODO*/ },
|
||||
shape = RoundedCornerShape(10.dp), containerColor = Color.White, contentColor = Color.Black) {
|
||||
AnimatedVisibility(
|
||||
visible = offset + 200 > floatingActionShowOffset + 100,
|
||||
enter = slideInVertically() + fadeIn(),
|
||||
exit = slideOutVertically() + fadeOut()
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = { /*TODO*/ },
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
containerColor = Color.White,
|
||||
contentColor = Color.Black
|
||||
) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
Text(text = "Скрыть завершенные задачи")
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import androidx.compose.material3.ButtonDefaults
|
|||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -28,17 +29,25 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.apollographql.apollo3.api.ApolloResponse
|
||||
import com.example.mpdriver.GetPlannedTasksIDsQuery
|
||||
import com.example.mpdriver.GetTaskByIdQuery
|
||||
import com.example.mpdriver.api.TaskApi
|
||||
import com.example.mpdriver.api.TaskResponse
|
||||
import com.example.mpdriver.api.apolloClient
|
||||
import com.example.mpdriver.components.ActiveButton
|
||||
import com.example.mpdriver.components.HeaderTabs
|
||||
import com.example.mpdriver.components.HeaderTabsData
|
||||
import com.example.mpdriver.components.Layout
|
||||
import com.example.mpdriver.components.StaleButton
|
||||
import com.example.mpdriver.components.Task
|
||||
import com.example.mpdriver.storage.CreateUpdateTaskData
|
||||
import com.example.mpdriver.storage.Database
|
||||
import com.example.mpdriver.type.StatusEnumQl
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Clock
|
||||
import java.time.LocalDateTime
|
||||
|
||||
|
||||
@Composable
|
||||
|
@ -50,26 +59,41 @@ fun TasksList(modifier: Modifier = Modifier, hostController: NavHostController)
|
|||
}
|
||||
|
||||
|
||||
var itemsList by remember {
|
||||
mutableStateOf<List<TaskResponse>>(emptyList())
|
||||
}
|
||||
api.getPlannedTasks {
|
||||
itemsList = it
|
||||
isLoading = false
|
||||
var itemsListResponse by remember {
|
||||
mutableStateOf(emptyList<GetPlannedTasksIDsQuery.Task>())
|
||||
}
|
||||
|
||||
|
||||
var activeTab by remember {
|
||||
mutableIntStateOf(0)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
val d = Database.tasks.filter { it -> it.status == StatusEnumQl.NOT_DEFINED}
|
||||
|
||||
itemsListResponse = when(d.count()) {
|
||||
0 -> emptyList()
|
||||
else -> d.map {
|
||||
val sbts = it.subtasks.map {sbt ->
|
||||
GetPlannedTasksIDsQuery.Subtask(sbt.id)
|
||||
}
|
||||
GetPlannedTasksIDsQuery.Task(id = it.id, sbts)
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
|
||||
if(isLoading) {
|
||||
Column (Modifier.fillMaxWidth().padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Column (
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||
}
|
||||
return
|
||||
}
|
||||
Layout(dataList = itemsList, header = {
|
||||
Layout(dataList = itemsListResponse, header = {
|
||||
HeaderTabs(
|
||||
tabsData = listOf(
|
||||
HeaderTabsData(0, "Запланированные"),
|
||||
|
@ -79,15 +103,24 @@ fun TasksList(modifier: Modifier = Modifier, hostController: NavHostController)
|
|||
activeTab = it
|
||||
}
|
||||
}) {
|
||||
Task(Modifier.padding(bottom = 10.dp), taskResponse = it) {
|
||||
Task(Modifier.padding(bottom = 10.dp), task_id = it.id.toLong()) {
|
||||
when (activeTab) {
|
||||
0 -> Button(
|
||||
onClick = {
|
||||
api.setTaskStatusInProgress(it.id!!) {
|
||||
MainScope().launch {
|
||||
hostController.navigate("feed")
|
||||
}
|
||||
}
|
||||
Database.createUpdateTaskDataLocally(
|
||||
CreateUpdateTaskData(
|
||||
it.id.toLong(),
|
||||
Clock.System.now().toEpochMilliseconds(),
|
||||
"InProgress"
|
||||
)
|
||||
)
|
||||
Database.createUpdateTaskDataLocally(
|
||||
CreateUpdateTaskData(
|
||||
it.subtasks[0].id.toLong(), Clock.System.now().toEpochMilliseconds(), "InProgress",
|
||||
)
|
||||
)
|
||||
hostController.navigate("feed")
|
||||
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
contentColor = Color.White,
|
||||
|
@ -96,7 +129,7 @@ fun TasksList(modifier: Modifier = Modifier, hostController: NavHostController)
|
|||
disabledContainerColor = Color.Gray
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
enabled = if (Database.tasks.filter { it.status == "InProgress" }.count() >= 1) false else true,
|
||||
// enabled = if (Database.tasks.filter { it.status == "InProgress" }.count() >= 1) false else true,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = "Приступить к выполнению")
|
||||
|
|
|
@ -1,43 +1,28 @@
|
|||
package com.example.mpdriver.storage
|
||||
|
||||
import com.example.mpdriver.api.TaskResponse
|
||||
import android.util.Log
|
||||
import com.benasher44.uuid.uuid4
|
||||
import com.example.mpdriver.FetchApplicationDataQuery
|
||||
import com.example.mpdriver.api.toJson
|
||||
import com.example.mpdriver.type.DateTime
|
||||
import com.example.mpdriver.type.StatusEnumQl
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.tencent.mmkv.MMKV
|
||||
import java.lang.reflect.Type
|
||||
|
||||
|
||||
data class CreateUpdateTaskData(
|
||||
val task_id: Long,
|
||||
val dt: Long,
|
||||
val status: String,
|
||||
val error_text: String? = null
|
||||
)
|
||||
|
||||
object Database {
|
||||
|
||||
val kv = MMKV.defaultMMKV()
|
||||
|
||||
var tasks: List<TaskResponse>
|
||||
get() {
|
||||
val data = kv.decodeString("tasks")
|
||||
data?.let { json ->
|
||||
val type: Type = object : TypeToken<List<TaskResponse>>() {}.type
|
||||
return Gson().fromJson(json, type)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
set(value) {
|
||||
val data = Gson().toJson(value)
|
||||
kv.encode("tasks", data)
|
||||
}
|
||||
|
||||
var planned_tasks: List<TaskResponse>
|
||||
get() {
|
||||
val data = kv.decodeString("planned_tasks")
|
||||
data?.let { json ->
|
||||
val type: Type = object : TypeToken<List<TaskResponse>>() {}.type
|
||||
return Gson().fromJson(json, type)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
set(value) {
|
||||
val data = Gson().toJson(value)
|
||||
println(value)
|
||||
println(data)
|
||||
kv.encode("planned_tasks", data)
|
||||
}
|
||||
val allKeys: Array<out String>?
|
||||
get() = kv.allKeys()
|
||||
|
||||
var access_token: String?
|
||||
get() {
|
||||
|
@ -52,4 +37,96 @@ object Database {
|
|||
set(value) {
|
||||
kv.encode("phone_number", value)
|
||||
}
|
||||
|
||||
|
||||
private inline fun <reified D> parseData(prefix: String): MutableList<D> {
|
||||
val returnedList = mutableListOf<D>()
|
||||
|
||||
for (key in allKeys!!) {
|
||||
if (key.startsWith(prefix)) {
|
||||
val data = kv.decodeString(key)
|
||||
data.let {
|
||||
returnedList.add(Gson().fromJson(it, D::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnedList
|
||||
}
|
||||
|
||||
val tasks: MutableList<FetchApplicationDataQuery.Task>
|
||||
get() = parseData("task:")
|
||||
|
||||
val notes: MutableList<FetchApplicationDataQuery.Note>
|
||||
get() = parseData("note:")
|
||||
|
||||
val subtasks: MutableList<FetchApplicationDataQuery.Subtask1>
|
||||
get() = parseData("subtask:")
|
||||
|
||||
|
||||
val updateTasks: MutableList<CreateUpdateTaskData>
|
||||
get() = parseData("update:task:")
|
||||
|
||||
fun createUpdateTaskDataLocally(d: CreateUpdateTaskData) {
|
||||
|
||||
kv.encode("update:task:${uuid4()}", d.toJson())
|
||||
val task = tasks.find { it.id == d.task_id.toString() }
|
||||
val sbt = subtasks.find { it.id == d.task_id.toString() }
|
||||
|
||||
task?.let {
|
||||
|
||||
val newTask = FetchApplicationDataQuery.Task(
|
||||
id = task.id,
|
||||
startPln = task.startPln,
|
||||
startFact = task.startFact,
|
||||
endPln = task.endPln,
|
||||
endFact = task.endFact,
|
||||
status = when (d.status) {
|
||||
"InProgress" -> StatusEnumQl.IN_PROGRESS
|
||||
"Completed" -> StatusEnumQl.COMPLETED
|
||||
"Cancelled" -> StatusEnumQl.CANCELLED
|
||||
else -> task.status
|
||||
},
|
||||
events = task.events,
|
||||
subtasks = task.subtasks,
|
||||
taskType = task.taskType,
|
||||
route = task.route,
|
||||
text = task.text
|
||||
)
|
||||
kv.encode("task:${newTask.id}", newTask.toJson())
|
||||
// Log.d("MMKV: task:${newTask.id}", kv.decodeString("task:${newTask.id}")!!)
|
||||
Log.d("MMKV.KEYS", allKeys!!.toMutableList().joinToString(", "))
|
||||
}
|
||||
|
||||
sbt?.let { s ->
|
||||
val newSubtask = FetchApplicationDataQuery.Subtask1(
|
||||
id = s.id,
|
||||
startPln = s.startPln,
|
||||
startFact = s.startFact,
|
||||
endPln = s.endPln,
|
||||
endFact = s.endFact,
|
||||
station = s.station,
|
||||
status = when (d.status) {
|
||||
"InProgress" -> StatusEnumQl.IN_PROGRESS
|
||||
"Completed" -> StatusEnumQl.COMPLETED
|
||||
"Cancelled" -> StatusEnumQl.CANCELLED
|
||||
else -> s.status
|
||||
},
|
||||
taskType = s.taskType,
|
||||
text = s.text
|
||||
)
|
||||
kv.encode("subtask:${newSubtask.id}", newSubtask.toJson())
|
||||
Log.d("MMKV.KEYS", allKeys!!.toMutableList().joinToString(", "))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun dropUpdates() {
|
||||
for (key in allKeys!!){
|
||||
if (key.startsWith("update:task:")) {
|
||||
kv.remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,4 +2,5 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||
id("com.google.gms.google-services") version "4.4.2" apply false
|
||||
}
|
Loading…
Reference in New Issue