Skip to content

Commit

Permalink
Merge pull request #58 from TeamHY2/Feature/#56-change-firestore-stru…
Browse files Browse the repository at this point in the history
…cture

[Feature]  firebase firestore 구조를 변경하고 캘린더 StudyDay를 불러와 반영합니다.
  • Loading branch information
librarywon authored Sep 3, 2024
2 parents 6344843 + 5086f81 commit 9e04183
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class Calendar(
DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT_PATTERN).withLocale(Locale.ENGLISH),
private val studyDays: List<StudyDay> = emptyList(),
) {
private var date: LocalDate = initDate
var date: LocalDate = initDate
private set

val now: String
get() = dateTimeFormatter.format(date)
Expand Down Expand Up @@ -44,6 +45,17 @@ class Calendar(
date = date.plusMonths(1)
}

fun copy(
initDate: LocalDate = this.date,
studyDays: List<StudyDay> = this.studyDays,
): Calendar {
return Calendar(
initDate = initDate,
dateTimeFormatter = this.dateTimeFormatter,
studyDays = studyDays,
)
}

companion object {
private const val DEFAULT_DATE_TIME_FORMAT_PATTERN = "MMM yyyy"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.teamhy2.main.data.repository

import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.DocumentReference
import com.google.firebase.firestore.FirebaseFirestore
import com.teamhy2.main.domain.model.StudyDay
import com.teamhy2.main.domain.model.toMap
import com.google.firebase.firestore.SetOptions
import com.teamhy2.main.domain.model.StudyDayResponse
import com.teamhy2.main.domain.repository.StudyDayRepository
import kotlinx.coroutines.tasks.await
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import javax.inject.Inject

Expand All @@ -31,21 +31,93 @@ class DefaultStudyDayRepository

val duration: Long = ChronoUnit.SECONDS.between(startTimeParsed, now)

val studyDay =
StudyDay(
date = LocalDate.now(),
secondDuration = duration,
)
val yearMonthFormatter = DateTimeFormatter.ofPattern(YEAR_MONTH_PATTERN)
val yearMonth = now.format(yearMonthFormatter)
val dayOfMonth = now.dayOfMonth.toString()

val userDocumentReference: DocumentReference =
firestore.collection(USERS_COLLECTION).document(uid)
val studyDayCollection: CollectionReference =
userDocumentReference.collection(STUDYDAY_COLLECTION)
studyDayCollection.add(studyDay.toMap()).await()
val studyDayDocument: DocumentReference =
userDocumentReference.collection(STUDYDAY_COLLECTION).document(yearMonth)
val dayDocument: DocumentReference =
studyDayDocument.collection(DAY_COLLECTION).document(dayOfMonth)

firestore.runTransaction { transaction ->
val daySnapshot = transaction.get(dayDocument)
val studyStartTimeList =
daySnapshot.get(STUDY_START_TIME_FIELD) as? List<String> ?: emptyList()
val studySecondDurationList =
daySnapshot.get(STUDY_SECOND_DURATION_FIELD) as? List<Long> ?: emptyList()

val monthSnapshot = transaction.get(studyDayDocument)
val currentMonthTotal = monthSnapshot.getLong(TOTAL_MONTH_STUDY_TIME_FIELD) ?: 0L

val userSnapshot = transaction.get(userDocumentReference)
val currentTotalStudyTime = userSnapshot.getLong(TOTAL_STUDY_TIME_FIELD) ?: 0L

// Day 문서에 대한 업데이트 (존재하지 않으면 새로 생성)
val updatedDayData = mutableMapOf<String, Any>()
updatedDayData[STUDY_START_TIME_FIELD] =
studyStartTimeList + startTimeParsed.toLocalTime().toString()
updatedDayData[STUDY_SECOND_DURATION_FIELD] = studySecondDurationList + duration
transaction.set(dayDocument, updatedDayData)

// Month 문서에 대한 업데이트 (존재하지 않으면 새로 생성)
val newMonthTotal = currentMonthTotal + duration
transaction.set(
studyDayDocument,
mapOf(TOTAL_MONTH_STUDY_TIME_FIELD to newMonthTotal),
SetOptions.merge(),
)

// User 문서에 대한 업데이트 (존재하지 않으면 새로 생성)
val newTotalStudyTime = currentTotalStudyTime + duration
transaction.set(
userDocumentReference,
mapOf(TOTAL_STUDY_TIME_FIELD to newTotalStudyTime),
SetOptions.merge(),
)
}.await()
}

override suspend fun getStudyDays(uid: String): Map<String, List<StudyDayResponse>> {
val userDocumentReference = firestore.collection(USERS_COLLECTION).document(uid)
val studyDayCollectionReference = userDocumentReference.collection(STUDYDAY_COLLECTION)

val studyDaysByMonthResponse = mutableMapOf<String, List<StudyDayResponse>>()
val studyDayDocuments = studyDayCollectionReference.get().await().documents

studyDayDocuments.forEach { yearMonthDoc ->
val yearMonth = yearMonthDoc.id
val daysCollection = yearMonthDoc.reference.collection(DAY_COLLECTION).get().await()

val studyDayResponses =
daysCollection.documents.map { dayDoc ->
val day = dayDoc.id.toInt()
val date =
LocalDate.of(
yearMonth.split("-")[0].toInt(),
yearMonth.split("-")[1].toInt(),
day,
)
val studyStartTimes = dayDoc.get(STUDY_START_TIME_FIELD) as List<String>
StudyDayResponse(date, studyStartTimes)
}

studyDaysByMonthResponse[yearMonth] = studyDayResponses
}

return studyDaysByMonthResponse
}

companion object {
private const val USERS_COLLECTION = "User"
private const val STUDYDAY_COLLECTION = "StudyDay"
private const val DAY_COLLECTION = "Day"
private const val YEAR_MONTH_PATTERN = "yyyy-MM"
private const val STUDY_START_TIME_FIELD = "studyStartTime"
private const val STUDY_SECOND_DURATION_FIELD = "studySecondDuration"
private const val TOTAL_MONTH_STUDY_TIME_FIELD = "totalMonthStudyTime"
private const val TOTAL_STUDY_TIME_FIELD = "totalStudyTime"
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.teamhy2.main.domain.model

import java.time.LocalDate

data class StudyDayResponse(
val date: LocalDate,
val studyStartTimes: List<String>,
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.teamhy2.main.domain.repository

import com.teamhy2.main.domain.model.StudyDayResponse

interface StudyDayRepository {
suspend fun addStudyDay(
uid: String,
startTime: String,
)

suspend fun getStudyDays(uid: String): Map<String, List<StudyDayResponse>>
}
1 change: 0 additions & 1 deletion main-presentation/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
plugins {
id("hongikyeolgong2.android.feature")
alias(libs.plugins.google.gms.google.services)
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.firebase.Firebase
import com.google.firebase.auth.auth
import com.hongikyeolgong2.calendar.model.Calendar
import com.hongikyeolgong2.calendar.model.StudyDay
import com.hongikyeolgong2.calendar.model.StudyRoomUsage
import com.teamhy2.feature.main.mapper.StudyDayMapper
import com.teamhy2.feature.main.model.MainUiState
import com.teamhy2.hongikyeolgong2.timer.prsentation.model.TimerUiModel
import com.teamhy2.main.domain.repository.StudyDayRepository
Expand All @@ -30,6 +29,8 @@ class MainViewModel
private val _mainUiState = MutableStateFlow(MainUiState())
val mainUiState: StateFlow<MainUiState> = _mainUiState.asStateFlow()

private val studyDays = mutableMapOf<String, List<StudyDay>>()

init {
getWiseSaying()
getCalendarData()
Expand All @@ -45,49 +46,63 @@ class MainViewModel
}

private fun getCalendarData() {
val initialCalendar =
Calendar(
studyDays =
listOf(
StudyDay(
date = LocalDate.now().withDayOfMonth(1),
studyRoomUsage = StudyRoomUsage.USED_ONCE,
),
),
val uid = Firebase.auth.currentUser?.uid ?: return
viewModelScope.launch {
val rawStudyDaysByMonth = studyDayRepository.getStudyDays(uid)
studyDays.clear()
studyDays.putAll(
rawStudyDaysByMonth.mapValues { entry ->
entry.value.map { studyDayResponse ->
StudyDayMapper.mapToStudyDay(studyDayResponse)
}
},
)
_mainUiState.value = mainUiState.value.copy(calendar = initialCalendar)
updateCurrentMonthStudyDays(LocalDate.now().year, LocalDate.now().monthValue)
}
}

private fun updateCurrentMonthStudyDays(
year: Int,
month: Int,
) {
val yearMonthKey = "$year-${String.format("%02d", month)}"
val studyDays = studyDays[yearMonthKey] ?: emptyList()
val updatedCalendar = _mainUiState.value.calendar.copy(studyDays = studyDays)
_mainUiState.value = _mainUiState.value.copy(calendar = updatedCalendar)
}

fun updateCalendarMonth(isNextMonth: Boolean) {
val updatedCalendar =
_mainUiState.value.calendar.apply {
if (isNextMonth) {
moveToNextMonth()
} else {
moveToPreviousMonth()
}
}
val year = updatedCalendar.date.year
val month = updatedCalendar.date.monthValue
updateCurrentMonthStudyDays(year, month)
}

fun updateTimePickerVisibility(isVisible: Boolean) {
_mainUiState.value = mainUiState.value.copy(isTimePickerVisible = isVisible)
_mainUiState.value = _mainUiState.value.copy(isTimePickerVisible = isVisible)
}

fun updateTimerRunning(isTimerRunning: Boolean) {
_mainUiState.value = mainUiState.value.copy(isTimerRunning = isTimerRunning)
_mainUiState.value = _mainUiState.value.copy(isTimerRunning = isTimerRunning)
}

fun updateSelectedTime(selectedTime: LocalTime) {
_mainUiState.value = mainUiState.value.copy(selectedTime = selectedTime)
_mainUiState.value = _mainUiState.value.copy(selectedTime = selectedTime)
}

fun updateStudyRoomExtendDialogVisibility(isVisible: Boolean) {
_mainUiState.value = mainUiState.value.copy(isStudyRoomExtendDialog = isVisible)
_mainUiState.value = _mainUiState.value.copy(isStudyRoomExtendDialog = isVisible)
}

fun updateStudyRoomEndDialogVisibility(isVisible: Boolean) {
_mainUiState.value = mainUiState.value.copy(isStudyRoomEndDialog = isVisible)
}

fun updateCalendarMonth(isNextMonth: Boolean) {
val updatedCalendar =
mainUiState.value.calendar.apply {
if (isNextMonth) {
moveToNextMonth()
} else {
moveToPreviousMonth()
}
}
_mainUiState.value = mainUiState.value.copy(calendar = updatedCalendar)
_mainUiState.value = _mainUiState.value.copy(isStudyRoomEndDialog = isVisible)
}

fun updateTimerStateFromTimerViewModel(timerState: TimerUiModel) {
Expand All @@ -100,10 +115,9 @@ class MainViewModel
}

fun addStudyDay() {
val uid = Firebase.auth.currentUser?.uid ?: ""
val startTime = mainUiState.value.startTime

if (uid.isNotEmpty() && startTime != null) {
val uid = Firebase.auth.currentUser?.uid ?: return
val startTime = _mainUiState.value.startTime
if (uid.isNotEmpty() && startTime.isNotEmpty()) {
viewModelScope.launch {
studyDayRepository.addStudyDay(uid, startTime)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.teamhy2.feature.main.mapper

import com.hongikyeolgong2.calendar.model.StudyDay
import com.hongikyeolgong2.calendar.model.StudyRoomUsage
import com.teamhy2.main.domain.model.StudyDayResponse

object StudyDayMapper {
fun mapToStudyDay(studyDayResponse: StudyDayResponse): StudyDay {
val studyRoomUsage =
when (studyDayResponse.studyStartTimes.size) {
1 -> StudyRoomUsage.USED_ONCE
2 -> StudyRoomUsage.USED_ONCE_EXTENDED_ONCE
3 -> StudyRoomUsage.USED_ONCE_EXTENDED_TWICE
else -> StudyRoomUsage.NEVER_USED
}
return StudyDay(date = studyDayResponse.date, studyRoomUsage = studyRoomUsage)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class DefaultUserRepository
DEPARTMENT_KEY to department,
EMAIL_KEY to Firebase.auth.currentUser?.email,
ID_KEY to Firebase.auth.currentUser?.uid,
TOTAL_STUDY_TIME_KEY to 0,
)

firestore.collection(FIREBASE_USER_COLLECTION)
Expand All @@ -68,5 +69,6 @@ class DefaultUserRepository
private const val DEPARTMENT_KEY = "department"
private const val EMAIL_KEY = "email"
private const val ID_KEY = "id"
private const val TOTAL_STUDY_TIME_KEY = "totalStudyTime"
}
}

0 comments on commit 9e04183

Please sign in to comment.