Skip to content

goal set feature and update db. #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ dependencies {
kapt(libs.hilt.compiler)
implementation(libs.hilt.navigation.compose)
implementation(libs.ehsannarmani.compose.charts)
implementation(libs.room.runtime)
implementation(libs.room.ktx)
kapt(libs.room.compiler)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.byteutility.dev.leetcode.plus.data.database

import androidx.room.Database
import androidx.room.RoomDatabase
import com.byteutility.dev.leetcode.plus.data.database.dao.WeeklyGoalDao
import com.byteutility.dev.leetcode.plus.data.database.entity.WeeklyGoalEntity

@Database(
entities = [WeeklyGoalEntity::class],
version = 1
)
abstract class LeetcodeDatabase: RoomDatabase() {
abstract fun weeklyGoalDao(): WeeklyGoalDao
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.byteutility.dev.leetcode.plus.data.database.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.byteutility.dev.leetcode.plus.data.database.entity.WeeklyGoalEntity

@Dao
interface WeeklyGoalDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertWeeklyGoal(weeklyGoalEntity: WeeklyGoalEntity)

@Query("SELECT * FROM weekly_goal LIMIT 1")
suspend fun getWeeklyGoal(): WeeklyGoalEntity

@Query("DELETE FROM weekly_goal")
suspend fun deleteWeeklyGoal()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.byteutility.dev.leetcode.plus.data.database.di

import android.content.Context
import androidx.room.Room
import com.byteutility.dev.leetcode.plus.data.database.LeetcodeDatabase
import com.byteutility.dev.leetcode.plus.data.database.dao.WeeklyGoalDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object LeetcodeDatabaseModule {

@Singleton
@Provides
fun provideLeetcodeDatabase(@ApplicationContext context: Context): LeetcodeDatabase {
return Room.databaseBuilder(
context.applicationContext,
LeetcodeDatabase::class.java,
"leetcode_database"
).build()
}

@Singleton
@Provides
fun provideWeeklyGoalDao(db: LeetcodeDatabase): WeeklyGoalDao {
return db.weeklyGoalDao()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.byteutility.dev.leetcode.plus.data.database.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.byteutility.dev.leetcode.plus.data.model.LeetCodeProblem
import com.byteutility.dev.leetcode.plus.data.model.WeeklyGoalPeriod
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

@Entity(tableName = "weekly_goal")
data class WeeklyGoalEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int = 0,
@ColumnInfo(name = "problems")
val problems: String,
@ColumnInfo(name = "period")
val period: String
) {
fun toProblems(): List<LeetCodeProblem> {
val type = object : TypeToken<List<LeetCodeProblem>>() {}.type
return Gson().fromJson(problems, type)
}
fun toWeeklyGoal(): WeeklyGoalPeriod {
return Gson().fromJson(period, WeeklyGoalPeriod::class.java)
}
companion object {
fun createWeeklyGoalEntity(
problems: List<LeetCodeProblem>,
period: WeeklyGoalPeriod,
): WeeklyGoalEntity {
val problemsJson = Gson().toJson(problems)
val periodJson = Gson().toJson(period)
return WeeklyGoalEntity(0, problemsJson, periodJson)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.byteutility.dev.leetcode.plus.data.model

data class WeeklyGoalPeriod(
val startDate: String,
val endDate: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.byteutility.dev.leetcode.plus.data.repository

import com.byteutility.dev.leetcode.plus.data.model.LeetCodeProblem
import com.byteutility.dev.leetcode.plus.data.model.WeeklyGoalPeriod

interface WeeklyGoalRepository {
suspend fun saveWeeklyGoal(
problems: List<LeetCodeProblem>,
period: WeeklyGoalPeriod,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.byteutility.dev.leetcode.plus.data.repository

import com.byteutility.dev.leetcode.plus.data.database.dao.WeeklyGoalDao
import com.byteutility.dev.leetcode.plus.data.database.entity.WeeklyGoalEntity
import com.byteutility.dev.leetcode.plus.data.model.LeetCodeProblem
import com.byteutility.dev.leetcode.plus.data.model.WeeklyGoalPeriod
import javax.inject.Inject

class WeeklyGoalRepositoryImpl @Inject constructor(
private val weeklyGoalDao: WeeklyGoalDao
): WeeklyGoalRepository {
override suspend fun saveWeeklyGoal(
problems: List<LeetCodeProblem>,
period: WeeklyGoalPeriod
) {
val entity = WeeklyGoalEntity.createWeeklyGoalEntity(problems, period)
weeklyGoalDao.insertWeeklyGoal(entity)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.byteutility.dev.leetcode.plus.data.di
package com.byteutility.dev.leetcode.plus.data.repository.di

import com.byteutility.dev.leetcode.plus.data.repository.ProblemsRepository
import com.byteutility.dev.leetcode.plus.data.repository.ProblemsRepositoryImpl
import com.byteutility.dev.leetcode.plus.data.repository.WeeklyGoalRepository
import com.byteutility.dev.leetcode.plus.data.repository.WeeklyGoalRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
Expand All @@ -17,4 +19,10 @@ abstract class RepositoryModule {
abstract fun provideProblemsRepository(
problemsRepository: ProblemsRepositoryImpl
): ProblemsRepository

@Binds
@Singleton
abstract fun provideWeeklyGoalRepository(
weeklyGoalRepository: WeeklyGoalRepositoryImpl
): WeeklyGoalRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.byteutility.dev.leetcode.plus.ui.dialogs

import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.byteutility.dev.leetcode.plus.data.model.WeeklyGoalPeriod
import java.time.LocalDate
import java.time.format.DateTimeFormatter

@Composable
fun WeeklyGoalSetDialog(
confirmed: (period: WeeklyGoalPeriod) -> Unit
) {
var showDialog by remember { mutableStateOf(true) }
val today = LocalDate.now()
val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy")
val startDate = today.format(formatter)
val endDate = today.plusDays(7).format(formatter)

if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text(text = "Confirming Weekly Goal") },
text = {
Text(
"Weekly goal from today ($startDate) until the next 7 days ($endDate)"
)
},
confirmButton = {
TextButton(
onClick = {
confirmed.invoke(WeeklyGoalPeriod(startDate, endDate))
showDialog = false
}
) {
Text("OK")
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.byteutility.dev.leetcode.plus.ui.targetset

import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
Expand All @@ -22,16 +24,20 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.byteutility.dev.leetcode.plus.data.model.LeetCodeProblem
import com.byteutility.dev.leetcode.plus.data.model.WeeklyGoalPeriod
import com.byteutility.dev.leetcode.plus.ui.dialogs.WeeklyGoalSetDialog

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand All @@ -44,16 +50,32 @@ fun SetWeeklyTargetScreen() {
TopAppBar(title = { Text(text = "Set Weekly Goals") })
},
) { innerPadding ->
ProblemSelection(Modifier.padding(innerPadding), problems = problems.value) { selectedProblems ->
println("Confirmed Problems: $selectedProblems")
var selectedProblems by remember { mutableStateOf<List<LeetCodeProblem>>(emptyList()) }
ProblemSelection(Modifier.padding(innerPadding), problems = problems.value) {
Log.i("SetWeeklyTargetScreen", "Problems selected for week")
selectedProblems = it
}
if (selectedProblems.isNotEmpty()) {
WeeklyGoalSetDialog {
viewModel.handleWeeklyGoalSet(selectedProblems, it)
}
}
}
}

@Composable
fun ProblemSelection(modifier: Modifier = Modifier, problems: List<LeetCodeProblem>, onConfirm: (List<LeetCodeProblem>) -> Unit) {
fun ProblemSelection(
modifier: Modifier = Modifier,
problems: List<LeetCodeProblem>,
onConfirm: (List<LeetCodeProblem>) -> Unit
) {
var selectedProblems by remember { mutableStateOf<List<LeetCodeProblem>>(emptyList()) }

var currentPage by remember { mutableIntStateOf(0) }
val itemsPerPage = 20
val totalPages = (problems.size + itemsPerPage - 1) / itemsPerPage
val displayedItems = problems.drop(currentPage * itemsPerPage).take(itemsPerPage)

Column(modifier = Modifier.background(Color.White).padding(16.dp)) {
LazyColumn(
modifier = Modifier
Expand All @@ -62,8 +84,7 @@ fun ProblemSelection(modifier: Modifier = Modifier, problems: List<LeetCodeProbl
.then(modifier),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(problems.size) { index ->
val problem = problems[index]
items(displayedItems) { problem ->
ProblemItem(
problem = problem,
isSelected = selectedProblems.contains(problem),
Expand All @@ -82,6 +103,30 @@ fun ProblemSelection(modifier: Modifier = Modifier, problems: List<LeetCodeProbl

Spacer(modifier = Modifier.height(16.dp))

Row(
modifier = Modifier
.fillMaxWidth()
.padding(6.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Button(
onClick = { if (currentPage > 0) currentPage-- },
enabled = currentPage > 0
) {
Text("Previous")
}

Text("Page ${currentPage + 1} of $totalPages")

Button(
onClick = { if (currentPage < totalPages - 1) currentPage++ },
enabled = currentPage < totalPages - 1
) {
Text("Next")
}
}

Button(
onClick = { onConfirm(selectedProblems) },
enabled = selectedProblems.size == 7,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.byteutility.dev.leetcode.plus.ui.targetset

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.byteutility.dev.leetcode.plus.data.model.LeetCodeProblem
import com.byteutility.dev.leetcode.plus.data.model.WeeklyGoalPeriod
import com.byteutility.dev.leetcode.plus.data.repository.ProblemsRepository
import com.byteutility.dev.leetcode.plus.network.RestApiService
import com.byteutility.dev.leetcode.plus.data.repository.WeeklyGoalRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -14,14 +14,24 @@ import javax.inject.Inject

@HiltViewModel
class SetWeeklyTargetViewModel @Inject constructor(
private val repository: ProblemsRepository
private val problemsRepository: ProblemsRepository,
private val weeklyGoalRepository: WeeklyGoalRepository
) : ViewModel() {

val problemsList = MutableStateFlow<List<LeetCodeProblem>>(emptyList())

init {
viewModelScope.launch(Dispatchers.IO) {
problemsList.value = repository.getProblems(limit = 3000)
problemsList.value = problemsRepository.getProblems(limit = 3000)
}
}

fun handleWeeklyGoalSet(
problems: List<LeetCodeProblem>,
period: WeeklyGoalPeriod
) {
viewModelScope.launch(Dispatchers.IO) {
weeklyGoalRepository.saveWeeklyGoal(problems, period)
}
}
}
Loading