Compare commits

...

3 Commits

24 changed files with 909 additions and 119 deletions

View File

@ -0,0 +1,208 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "fd664bd9d0a27511fb20675444548276",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user_login` TEXT NOT NULL, `user_password` TEXT NOT NULL, `user_role` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "login",
"columnName": "user_login",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "user_password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "role",
"columnName": "user_role",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "orders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `order_status` TEXT NOT NULL, `user_id` INTEGER, `date` INTEGER NOT NULL, FOREIGN KEY(`user_id`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "order_status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_orders_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_orders_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"user_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "products",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `product_title` TEXT NOT NULL, `product_price` INTEGER NOT NULL, `product_old_price` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "product_title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "price",
"columnName": "product_price",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "oldPrice",
"columnName": "product_old_price",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "order_product",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`order_id` INTEGER NOT NULL, `product_id` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, `total_price` INTEGER NOT NULL, PRIMARY KEY(`order_id`, `product_id`))",
"fields": [
{
"fieldPath": "orderId",
"columnName": "order_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "productId",
"columnName": "product_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "quantity",
"columnName": "quantity",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "totalPrice",
"columnName": "total_price",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"order_id",
"product_id"
]
},
"indices": [
{
"name": "index_order_product_order_id",
"unique": false,
"columnNames": [
"order_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_order_product_order_id` ON `${TABLE_NAME}` (`order_id`)"
},
{
"name": "index_order_product_product_id",
"unique": false,
"columnNames": [
"product_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_order_product_product_id` ON `${TABLE_NAME}` (`product_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fd664bd9d0a27511fb20675444548276')"
]
}
}

View File

@ -0,0 +1,208 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "1ce8ad5b9b5decfebb42bc2f3c33393d",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user_login` TEXT NOT NULL, `user_password` TEXT NOT NULL, `user_role` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "login",
"columnName": "user_login",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "user_password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "role",
"columnName": "user_role",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "orders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `order_status` TEXT NOT NULL, `user_id` INTEGER, `date` TEXT NOT NULL, FOREIGN KEY(`user_id`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "order_status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_orders_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_orders_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"user_id"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "products",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `product_title` TEXT NOT NULL, `product_price` INTEGER NOT NULL, `product_old_price` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "product_title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "price",
"columnName": "product_price",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "oldPrice",
"columnName": "product_old_price",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "order_product",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`order_id` INTEGER NOT NULL, `product_id` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, `total_price` INTEGER NOT NULL, PRIMARY KEY(`order_id`, `product_id`))",
"fields": [
{
"fieldPath": "orderId",
"columnName": "order_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "productId",
"columnName": "product_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "quantity",
"columnName": "quantity",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "totalPrice",
"columnName": "total_price",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"order_id",
"product_id"
]
},
"indices": [
{
"name": "index_order_product_order_id",
"unique": false,
"columnNames": [
"order_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_order_product_order_id` ON `${TABLE_NAME}` (`order_id`)"
},
{
"name": "index_order_product_product_id",
"unique": false,
"columnNames": [
"product_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_order_product_product_id` ON `${TABLE_NAME}` (`product_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1ce8ad5b9b5decfebb42bc2f3c33393d')"
]
}
}

View File

@ -0,0 +1,196 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "12eb76256f54b2c513a3073a2e582c4c",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user_login` TEXT NOT NULL, `user_password` TEXT NOT NULL, `user_role` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "login",
"columnName": "user_login",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "user_password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "role",
"columnName": "user_role",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "orders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `order_status` TEXT NOT NULL, `user_id` INTEGER, `date` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "order_status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_orders_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_orders_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "products",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `product_title` TEXT NOT NULL, `product_price` INTEGER NOT NULL, `product_old_price` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "product_title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "price",
"columnName": "product_price",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "oldPrice",
"columnName": "product_old_price",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "order_product",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`order_id` INTEGER NOT NULL, `product_id` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, `total_price` INTEGER NOT NULL, PRIMARY KEY(`order_id`, `product_id`))",
"fields": [
{
"fieldPath": "orderId",
"columnName": "order_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "productId",
"columnName": "product_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "quantity",
"columnName": "quantity",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "totalPrice",
"columnName": "total_price",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"order_id",
"product_id"
]
},
"indices": [
{
"name": "index_order_product_order_id",
"unique": false,
"columnNames": [
"order_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_order_product_order_id` ON `${TABLE_NAME}` (`order_id`)"
},
{
"name": "index_order_product_product_id",
"unique": false,
"columnNames": [
"product_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_order_product_product_id` ON `${TABLE_NAME}` (`product_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '12eb76256f54b2c513a3073a2e582c4c')"
]
}
}

View File

@ -3,6 +3,8 @@ package com.example.shawarma
import android.app.Application
import androidx.room.Room
import com.example.shawarma.data.api.MyServerService
import com.example.shawarma.data.api.repos.RestOrderProductRepository
import com.example.shawarma.data.api.repos.RestOrderRepository
import com.example.shawarma.data.api.repos.RestProductRepository
import com.example.shawarma.data.api.repos.RestUserRepository
import com.example.shawarma.data.db.AppDatabase
@ -49,8 +51,8 @@ object AppModule {
@Provides
@Singleton
fun provideProductRepository(db: AppDatabase) : ProductRepository {
return ProductRepository(database = db, db.productDao(), db.orderProductDao())
fun provideProductRepository(db: AppDatabase, restProductRepository: RestProductRepository) : ProductRepository {
return ProductRepository(database = db, db.productDao(), db.orderProductDao(), restProductRepository)
}
@Provides
@ -61,14 +63,26 @@ object AppModule {
@Provides
@Singleton
fun provideOrderRepository(db: AppDatabase) : OrderRepository {
return OrderRepository(db.orderDao())
fun provideOrderRepository(db: AppDatabase, restOrderRepository: RestOrderRepository) : OrderRepository {
return OrderRepository(db.orderDao(), db.productDao(), db.orderProductDao(), restOrderRepository)
}
@Provides
@Singleton
fun provideOrderProductRepository(db: AppDatabase) : OrderProductRepository {
return OrderProductRepository(db.orderProductDao())
fun provideRestOrderRepository(service: MyServerService) : RestOrderRepository {
return RestOrderRepository(service)
}
@Provides
@Singleton
fun provideOrderProductRepository(db: AppDatabase, restOrderProductRepository: RestOrderProductRepository) : OrderProductRepository {
return OrderProductRepository(db.orderProductDao(), restOrderProductRepository)
}
@Provides
@Singleton
fun provideRestOrderProductRepository(service: MyServerService) : RestOrderProductRepository {
return RestOrderProductRepository(service)
}

View File

@ -1,6 +1,9 @@
package com.example.shawarma.data.api
import com.example.shawarma.data.api.models.OrderModelRemote
import com.example.shawarma.data.api.models.OrderProductRemote
import com.example.shawarma.data.api.models.ProductListResponse
import com.example.shawarma.data.api.models.ProductModelRemote
import com.example.shawarma.data.api.models.TokenModelRemote
import com.example.shawarma.data.api.models.UserModelRemote
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
@ -9,13 +12,18 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
interface MyServerService {
//
// USER
//
@POST("register")
suspend fun createUser(
@Body user: UserModelRemote,
@ -31,6 +39,13 @@ interface MyServerService {
@Body user: UserModelRemote
) :UserModelRemote?
@GET("user")
suspend fun getUser(@Header("Authorization") token: String) : UserModelRemote?
//
// PRODUCTS
//
@GET("user/products/{after}")
suspend fun getProductsList(@Path("after") after: Int, @Header("Authorization") token: String) : ProductListResponse
@ -40,6 +55,40 @@ interface MyServerService {
@GET("user/items/{after}")
suspend fun getItemsList(@Path("after") after: Int, @Header("Authorization") token: String) : ProductListResponse
@POST("product")
suspend fun insertProduct(
@Body product: ProductModelRemote,
@Header("Authorization") token: String
) : ProductModelRemote
@PUT("product")
suspend fun updateProduct(
@Body product: ProductModelRemote,
@Header("Authorization") token: String
): ProductModelRemote
@DELETE("product/{id}")
suspend fun deleteProduct(@Path("id") id: Int, @Header("Authorization") token: String)
//
// ORDERS
//
@GET("order/unpaid")
suspend fun getUnpaidOrder(@Header("Authorization") token: String) : OrderModelRemote
@POST("order")
suspend fun insertOrder(@Body order: OrderModelRemote, @Header("Authorization") token: String) : OrderModelRemote?
//
// ORDER PRODUCTS
//
@POST("order/product")
suspend fun insertOrderProduct(@Body orderProduct: OrderProductRemote, @Header("Authorization") token: String)
@PUT("order/product")
suspend fun updateOrderProduct(@Body orderProduct: OrderProductRemote, @Header("Authorization") token: String)
companion object {
private const val BASE_URL = "https://10.0.2.2:80/api/"

View File

@ -27,30 +27,16 @@ class ProductRemoteMediator (
state: PagingState<Int, ProductModel>
): MediatorResult {
return try {
// The network load method takes an optional `after=<user.id>` parameter. For every
// page after the first, we pass the last user ID to let it continue from where it
// left off. For REFRESH, pass `null` to load the first page.
var loadKey = when (loadType) {
LoadType.REFRESH -> null
// In this example, we never need to prepend, since REFRESH will always load the
// first page in the list. Immediately return, reporting end of pagination.
LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
LoadType.APPEND -> {
val lastItem = state.lastItemOrNull()
?: return MediatorResult.Success(endOfPaginationReached = true)
// We must explicitly check if the last item is `null` when appending,
// since passing `null` to networkService is only valid for initial load.
// If lastItem is `null` it means no items were loaded after the initial
// REFRESH and there are no more items to load.
lastItem.id
}
}
// Suspending network load via Retrofit. This doesn't need to be wrapped in a
// withContext(Dispatcher.IO) { ... } block since Retrofit's Coroutine CallAdapter
// dispatches on a worker thread.
if (loadKey == null) {
loadKey = 0
}
@ -74,8 +60,6 @@ class ProductRemoteMediator (
productDao.deleteByQuery(query)
}
// Insert new users into database, which invalidates the current
// PagingData, allowing Paging to present the updates in the DB.
val products = mutableListOf<ProductModel>()
for (prod in response.products) {
products.add(prod.toProductModel())

View File

@ -0,0 +1,42 @@
package com.example.shawarma.data.api.models
import com.example.shawarma.data.models.OrderModel
import com.example.shawarma.data.models.OrderProductModel
import kotlinx.serialization.Serializable
@Serializable
data class OrderModelRemote(
val id: Int? = null,
val status: String = "",
val user_id: Int? = -1,
val date: String = "",
val order_products: List<OrderProductRemote> = listOf()
)
fun OrderModelRemote.toOrderModel() : OrderModel = OrderModel(
id, status, user_id, date = date
)
fun OrderModel.toOrderModelRemote() : OrderModelRemote = OrderModelRemote(
id, status, userId, date.toString()
)
@Serializable
data class OrderProductRemote(
val id: Int? = null,
val order_id: Int = -1,
val product_id: Int = -1,
val quantity: Int = 0,
val total_price: Int = 0,
val order: OrderModelRemote? = null,
val product: ProductModelRemote? = null
)
fun OrderProductRemote.toOrderProductModel() : OrderProductModel = OrderProductModel(
order_id, product_id, quantity, total_price
)
fun OrderProductModel.toOrderProductRemote() : OrderProductRemote = OrderProductRemote(
order_id = orderId, product_id = productId, quantity = quantity, total_price = totalPrice
)

View File

@ -5,10 +5,12 @@ import kotlinx.serialization.Serializable
@Serializable
data class ProductModelRemote(
val id: Int = 0,
val id: Int? = null,
val title: String = "",
val price: Int = 0,
val old_price: Int? = null
val old_price: Int? = null,
val order_products: List<OrderProductRemote> = listOf()
)
@Serializable
@ -22,5 +24,5 @@ fun ProductModelRemote.toProductModel(): ProductModel = ProductModel(
)
fun ProductModel.toProductModelRemote(): ProductModelRemote = ProductModelRemote(
title = title, price = price, old_price = oldPrice
title = title, price = price, old_price = oldPrice, id = id
)

View File

@ -0,0 +1,17 @@
package com.example.shawarma.data.api.repos
import com.example.shawarma.data.api.MyServerService
import com.example.shawarma.data.api.models.OrderProductRemote
import javax.inject.Inject
class RestOrderProductRepository @Inject constructor(
private val service: MyServerService
) {
suspend fun insertOrderProduct(token: String, orderProduct: OrderProductRemote) {
service.insertOrderProduct(orderProduct, token)
}
suspend fun updateOrderProduct(token: String, orderProduct: OrderProductRemote) {
service.updateOrderProduct(orderProduct, token)
}
}

View File

@ -0,0 +1,17 @@
package com.example.shawarma.data.api.repos
import com.example.shawarma.data.api.MyServerService
import com.example.shawarma.data.api.models.OrderModelRemote
import javax.inject.Inject
class RestOrderRepository@Inject constructor(
private val service: MyServerService
) {
suspend fun getUnpaidOrder(token:String) : OrderModelRemote {
return service.getUnpaidOrder(token)
}
suspend fun insertOrder(token: String, order: OrderModelRemote): OrderModelRemote? {
return service.insertOrder(order, token)
}
}

View File

@ -1,10 +1,22 @@
package com.example.shawarma.data.api.repos
import com.example.shawarma.data.api.MyServerService
import com.example.shawarma.data.api.models.toProductModelRemote
import com.example.shawarma.data.models.ProductModel
import javax.inject.Inject
class RestProductRepository @Inject constructor(
private val service: MyServerService
) {
suspend fun insert(product: ProductModel, token: String) {
service.insertProduct(product.toProductModelRemote(), token)
}
suspend fun update(product: ProductModel, token: String) {
service.updateProduct((product.toProductModelRemote()), token)
}
suspend fun delete(id: Int, token: String) {
service.deleteProduct(id, token)
}
}

View File

@ -21,4 +21,8 @@ class RestUserRepository @Inject constructor(
suspend fun checkLogin(user: UserModel): UserModelRemote? {
return service.checkLogin(user.toUserModelRemote())
}
suspend fun getUser(token: String) : UserModelRemote? {
return service.getUser(token)
}
}

View File

@ -1,6 +1,7 @@
package com.example.shawarma.data.db
import android.content.Context
import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@ -26,8 +27,9 @@ import kotlinx.coroutines.launch
ProductModel::class,
OrderProductModel::class,
],
version = 1,
exportSchema = false
version = 3,
autoMigrations = [AutoMigration (from = 1, to = 2), AutoMigration (from = 2, to = 3)],
exportSchema = true
)
@TypeConverters(Converter::class)
abstract class AppDatabase : RoomDatabase() {

View File

@ -2,19 +2,10 @@ package com.example.shawarma.data.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import java.util.Date
@Entity(
tableName = "orders",
foreignKeys = [
ForeignKey(
entity = UserModel::class,
parentColumns = ["id"],
childColumns = ["user_id"],
)
]
tableName = "orders"
)
data class OrderModel(
@PrimaryKey(autoGenerate = true)
@ -25,7 +16,7 @@ data class OrderModel(
@ColumnInfo(name = "user_id", index = true)
val userId: Int?,
@ColumnInfo(name = "date")
val date: Date
val date: String
)
fun getOrdersByUserId() : List<OrderModel> {

View File

@ -1,17 +1,22 @@
package com.example.shawarma.data.repos
import com.example.shawarma.data.api.models.toOrderProductRemote
import com.example.shawarma.data.api.repos.RestOrderProductRepository
import com.example.shawarma.data.interfaces.dao.OrderProductDao
import com.example.shawarma.data.models.OrderProductModel
import javax.inject.Inject
class OrderProductRepository @Inject constructor(
private val orderProductDao: OrderProductDao
private val orderProductDao: OrderProductDao,
private val restOrderProductRepository: RestOrderProductRepository
) {
suspend fun insert(order: OrderProductModel) {
return orderProductDao.insert(order)
suspend fun insert(token: String, orderProduct: OrderProductModel) {
orderProductDao.insert(orderProduct)
return restOrderProductRepository.insertOrderProduct(token, orderProduct.toOrderProductRemote())
}
suspend fun update(order: OrderProductModel) {
return orderProductDao.update(order)
suspend fun update(token: String, orderProduct: OrderProductModel) {
orderProductDao.update(orderProduct)
return restOrderProductRepository.updateOrderProduct(token, orderProduct.toOrderProductRemote())
}
suspend fun delete(order: OrderProductModel) {
return orderProductDao.delete(order)

View File

@ -1,16 +1,31 @@
package com.example.shawarma.data.repos
import com.example.shawarma.data.api.models.toOrderModel
import com.example.shawarma.data.api.models.toOrderModelRemote
import com.example.shawarma.data.api.models.toOrderProductModel
import com.example.shawarma.data.api.models.toProductModel
import com.example.shawarma.data.api.repos.RestOrderRepository
import com.example.shawarma.data.interfaces.dao.OrderDao
import com.example.shawarma.data.interfaces.dao.OrderProductDao
import com.example.shawarma.data.interfaces.dao.ProductDao
import com.example.shawarma.data.models.OrderModel
import com.example.shawarma.data.models.OrderWithProducts
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import javax.inject.Inject
class OrderRepository @Inject constructor(
private val orderDao: OrderDao
private val orderDao: OrderDao,
private val productDao: ProductDao,
private val orderProductDao: OrderProductDao,
private val restRepository: RestOrderRepository
){
suspend fun insert(order: OrderModel): Long {
return orderDao.insert(order)
suspend fun insert(token: String, order: OrderModel): OrderModel? {
val result = restRepository.insertOrder(token, order.toOrderModelRemote())
if (result == null) {
return result
}
return result.toOrderModel()
}
suspend fun update(order:OrderModel) {
return orderDao.update(order)
@ -27,8 +42,20 @@ class OrderRepository @Inject constructor(
fun getByUserId(userId: Int): Flow<List<OrderWithProducts>> {
return orderDao.getByUserId(userId)
}
fun getUnpaidByUser(userId: Int) : Flow<OrderWithProducts?> {
return orderDao.getUnpaidByUser(userId)
suspend fun getUnpaidByUser(token: String) : OrderWithProducts? {
val order = restRepository.getUnpaidOrder(token)
if (orderDao.getUnpaidByUser(order.user_id!!).first() != null) {
return orderDao.getUnpaidByUser(order.user_id!!).first()
}
if (order!!.id != null) {
orderDao.insert(order.toOrderModel())
for (product in order.order_products) {
orderProductDao.insert(product.toOrderProductModel())
productDao.insert(product.product!!.toProductModel())
}
return orderDao.getUnpaidByUser(order.user_id!!).first()
}
return null
}
fun getPaidByUser(userId: Int) : Flow<List<OrderWithProducts>> {
return orderDao.getPaidByUser(userId)

View File

@ -6,6 +6,7 @@ import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.shawarma.data.api.MyServerService
import com.example.shawarma.data.api.mediators.ProductRemoteMediator
import com.example.shawarma.data.api.repos.RestProductRepository
import com.example.shawarma.data.db.AppDatabase
import com.example.shawarma.data.interfaces.dao.OrderProductDao
import com.example.shawarma.data.interfaces.dao.ProductDao
@ -16,17 +17,19 @@ import javax.inject.Inject
class ProductRepository @Inject constructor(
private val database: AppDatabase,
private val productDao: ProductDao,
private val orderProductDao: OrderProductDao
private val orderProductDao: OrderProductDao,
private val restRepository: RestProductRepository
) {
suspend fun insert(product: ProductModel) {
return productDao.insert(product)
suspend fun insert(product: ProductModel, token: String) {
return restRepository.insert(product, token)
}
suspend fun update(product: ProductModel) {
return productDao.update(product)
suspend fun update(product: ProductModel, token: String) {
return restRepository.update(product, token)
}
suspend fun delete(product: ProductModel) {
orderProductDao.deleteByProductId(product.id!!)
return productDao.delete(product)
suspend fun delete(product: ProductModel, token: String) {
//orderProductDao.deleteByProductId(product.id!!)
productDao.delete(product)
return restRepository.delete(product.id!!, token)
}
fun getById(id: Int): Flow<ProductModel> {
return productDao.getById(id)
@ -51,12 +54,14 @@ class ProductRepository @Inject constructor(
remoteMediator = ProductRemoteMediator(database = database, serverService = MyServerService.getInstance(), query = "discounts", token = token)
).flow
fun getAllItemsPaged(): Flow<PagingData<ProductModel>> = Pager(
@OptIn(ExperimentalPagingApi::class)
fun getAllItemsPaged(token: String): Flow<PagingData<ProductModel>> = Pager(
config = PagingConfig(
pageSize = 6,
enablePlaceholders = false
),
pagingSourceFactory = productDao::getPagedItems
pagingSourceFactory = productDao::getPagedItems,
remoteMediator = ProductRemoteMediator(database = database, serverService = MyServerService.getInstance(), query = "items", token = token)
).flow
}

View File

@ -167,7 +167,7 @@ fun ProductCard(product: ProductModel){
)
Button(
onClick = {
product.id?.let { homeViewModel.addProductToCart(it, preferencesManager.getData("user_id", "null")) }
product.id?.let { homeViewModel.addProductToCart(it, preferencesManager.getData("token", "null")) }
},
colors = ButtonDefaults.buttonColors(
backgroundColor = MyLightYellow,

View File

@ -20,6 +20,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
@ -27,6 +28,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import com.example.shawarma.data.sharedpref.PreferencesManager
import com.example.shawarma.ui.theme.MarckFamily
import com.example.shawarma.ui.theme.MyLightYellow
import com.example.shawarma.ui.theme.MyMainBackground
@ -49,6 +51,9 @@ fun ProductScreen(navHostController: NavHostController, productId: Int?) {
@Composable
fun ProductWidget(navHostController: NavHostController, productId: Int?) {
val preferencesManager = PreferencesManager(LocalContext.current)
val searchToken = preferencesManager.getData("token", "")
val title = remember { mutableStateOf(TextFieldValue(""))}
val price = remember { mutableStateOf(TextFieldValue(""))}
val oldPrice = remember { mutableStateOf(TextFieldValue(""))}
@ -71,8 +76,10 @@ fun ProductWidget(navHostController: NavHostController, productId: Int?) {
if (product != null) {
title.value = TextFieldValue(text = product.title)
price.value = TextFieldValue(text = product.price.toString())
if (product.oldPrice != null) {
oldPrice.value = TextFieldValue(text = product.oldPrice.toString())
}
}
Box(
modifier = Modifier
@ -140,9 +147,9 @@ fun ProductWidget(navHostController: NavHostController, productId: Int?) {
shape = RoundedCornerShape(20.dp),
onClick = {
if (product == null)
productViewModel.addProduct(title.value.text, price.value.text.toInt(), if (!oldPrice.value.text.isNullOrEmpty()) oldPrice.value.text.toInt() else null)
productViewModel.addProduct(title.value.text, price.value.text.toInt(), if (!oldPrice.value.text.isNullOrEmpty()) oldPrice.value.text.toInt() else null, searchToken)
else
productViewModel.updateProduct(title.value.text, price.value.text.toInt(), if (!oldPrice.value.text.isNullOrEmpty()) oldPrice.value.text.toInt() else null)
productViewModel.updateProduct(title.value.text, price.value.text.toInt(), if (!oldPrice.value.text.isNullOrEmpty()) oldPrice.value.text.toInt() else null, searchToken)
}
) {
Text(

View File

@ -24,6 +24,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
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.unit.dp
@ -35,6 +36,7 @@ import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.shawarma.R
import com.example.shawarma.data.models.ProductModel
import com.example.shawarma.data.sharedpref.PreferencesManager
import com.example.shawarma.ui.theme.MarckFamily
import com.example.shawarma.ui.theme.MyLightRed
import com.example.shawarma.ui.theme.MyLightYellow
@ -59,9 +61,12 @@ fun ProductsScreen(navHostController: NavHostController) {
@Composable
fun ProductsList(navHostController: NavHostController){
val preferencesManager = PreferencesManager(LocalContext.current)
val searchToken = preferencesManager.getData("token", "")
val productsViewModel: ProductsViewModel = hiltViewModel<ProductsViewModel>()
val products = productsViewModel.productListUiState.collectAsLazyPagingItems()
val products = productsViewModel.getItemsList(searchToken).collectAsLazyPagingItems()
Box(
modifier = Modifier
@ -108,7 +113,7 @@ fun ProductsList(navHostController: NavHostController){
products.itemCount,
key = products.itemKey()
) { index ->
ProductItem(products[index]!!, navHostController)
ProductItem(products[index]!!, navHostController, productsViewModel, searchToken)
Spacer(modifier = Modifier.height(20.dp))
if (index == products.itemCount - 1) {
Spacer(modifier = Modifier.height(70.dp))
@ -120,9 +125,7 @@ fun ProductsList(navHostController: NavHostController){
}
@Composable
fun ProductItem(product: ProductModel, navHostController: NavHostController){
val productsViewModel: ProductsViewModel = hiltViewModel<ProductsViewModel>()
fun ProductItem(product: ProductModel, navHostController: NavHostController, productsViewModel: ProductsViewModel, token: String){
Card(
border = BorderStroke(width = 2.dp, color = MyOrange),
shape = RoundedCornerShape(size = 20.dp),
@ -189,7 +192,7 @@ fun ProductItem(product: ProductModel, navHostController: NavHostController){
backgroundColor = MyLightRed
),
onClick = {
productsViewModel.deleteProduct(product)
productsViewModel.deleteProduct(product, token)
}
) {
Icon(

View File

@ -25,17 +25,17 @@ class CartViewModel @Inject constructor(
get() = _paidOrders
fun getOrders(userId: String) {
if (userId == "null") return
viewModelScope.launch {
orderRepository.getUnpaidByUser(userId.toInt()).collect {
_unpaidOrder.postValue(it)
}
}
viewModelScope.launch {
orderRepository.getPaidByUser(userId.toInt()).collect {
_paidOrders.postValue(it)
}
}
// if (userId == "null") return
// viewModelScope.launch {
// orderRepository.getUnpaidByUser(userId.toInt()).collect {
// _unpaidOrder.postValue(it)
// }
// }
// viewModelScope.launch {
// orderRepository.getPaidByUser(userId.toInt()).collect {
// _paidOrders.postValue(it)
// }
// }
}
fun payForOrder(order: OrderWithProducts) {
val model = order.order
@ -46,28 +46,28 @@ class CartViewModel @Inject constructor(
}
fun removeProductFromOrder(order: OrderWithProducts, productId: Int) {
val model = order.orderWithProducts[productId].orderProductModel
if(model.quantity == 1) {
// delete
viewModelScope.launch {
orderProductRepository.delete(model)
}
}
else{
// update
model.quantity -= 1
viewModelScope.launch {
orderProductRepository.update(model)
}
}
// val model = order.orderWithProducts[productId].orderProductModel
// if(model.quantity == 1) {
// // delete
// viewModelScope.launch {
// orderProductRepository.delete(model)
// }
// }
// else{
// // update
// model.quantity -= 1
// viewModelScope.launch {
// orderProductRepository.update(model)
// }
// }
}
fun addProductToOrder(order: OrderWithProducts, productId: Int) {
val model = order.orderWithProducts[productId].orderProductModel
// update
model.quantity += 1
viewModelScope.launch {
orderProductRepository.update(model)
}
// val model = order.orderWithProducts[productId].orderProductModel
// // update
// model.quantity += 1
// viewModelScope.launch {
// orderProductRepository.update(model)
// }
}
}

View File

@ -34,18 +34,18 @@ class HomeViewModel @Inject constructor(
}
fun addProductToCart(productId: Int, userId: String) {
if (userId == "null") return
fun addProductToCart(productId: Int, token: String) {
viewModelScope.launch {
val product = productRepository.getById(productId).first()
val order = orderRepository.getUnpaidByUser(userId.toInt()).first()
val order = orderRepository.getUnpaidByUser(token)
if (order == null) {
val calendar: Calendar = Calendar.getInstance()
calendar.time = Date()
calendar.add(Calendar.HOUR_OF_DAY, 4)
val newOrderId = orderRepository.insert(OrderModel(null, OrderStatus.Неоплачено.name, userId.toInt(),calendar.time))
orderProductRepository.insert(OrderProductModel(newOrderId.toInt(), productId, 1, product.price))
val newOrder = orderRepository.insert(token, OrderModel(null, OrderStatus.Неоплачено.name, null, calendar.time.toString()))
if (newOrder != null) {
orderProductRepository.insert(token, OrderProductModel(newOrder.id!!, productId, 1, product.price))
}
}
else {
var isAlreadyAdded = false
@ -54,12 +54,12 @@ class HomeViewModel @Inject constructor(
val model = prod.orderProductModel
model.quantity += 1
model.totalPrice += prod.product.price
orderProductRepository.update(model)
orderProductRepository.update(token, model)
isAlreadyAdded = true
}
}
if (!isAlreadyAdded) {
orderProductRepository.insert(OrderProductModel(order.order.id!!, productId, 1, product.price))
orderProductRepository.insert(token, OrderProductModel(order.order.id!!, productId, 1, product.price))
}
}
}

View File

@ -22,9 +22,9 @@ class ProductViewModel @Inject constructor(
val product: LiveData<ProductModel>
get() = _product
fun addProduct(name: String, price: Int, oldPrice: Int? = null) {
fun addProduct(name: String, price: Int, oldPrice: Int? = null, token: String) {
viewModelScope.launch {
productRepository.insert(ProductModel(null, name, price, oldPrice))
productRepository.insert(ProductModel(null, name, price, oldPrice), token)
_addingProductState.postValue(true)
}
}
@ -37,9 +37,9 @@ class ProductViewModel @Inject constructor(
}
}
fun updateProduct(name: String, price: Int, oldPrice: Int? = null) {
fun updateProduct(name: String, price: Int, oldPrice: Int? = null, token: String) {
viewModelScope.launch {
productRepository.update(ProductModel(product.value?.id, name, price, oldPrice))
productRepository.update(ProductModel(product.value?.id, name, price, oldPrice), token)
_addingProductState.postValue(true)
}
}

View File

@ -1,7 +1,5 @@
package com.example.shawarma.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
@ -16,15 +14,14 @@ import javax.inject.Inject
class ProductsViewModel @Inject constructor(
private val productRepository: ProductRepository
) : ViewModel() {
private val _products = MutableLiveData<List<ProductModel>>()
val products: LiveData<List<ProductModel>>
get() = _products
val productListUiState: Flow<PagingData<ProductModel>> = productRepository.getAllItemsPaged()
fun getItemsList(token:String): Flow<PagingData<ProductModel>> {
return productRepository.getAllItemsPaged(token)
}
fun deleteProduct(product: ProductModel) {
fun deleteProduct(product: ProductModel, token: String) {
viewModelScope.launch {
productRepository.delete(product)
productRepository.delete(product, token)
}
}