Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.example.hangsha_android.data.network.model.MemoResponse
import com.example.hangsha_android.data.network.model.UpdateMemoRequest
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.Path
Expand All @@ -25,4 +26,9 @@ interface MemoApi {
@Path("memoId") memoId: Long,
@Body request: UpdateMemoRequest
): Response<MemoResponse>

@DELETE("api/v1/memos/{memoId}")
suspend fun deleteMemo(
@Path("memoId") memoId: Long
): Response<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ import com.google.gson.annotations.SerializedName

data class UpdateMemoRequest(
@SerializedName("content")
val content: String
val content: String? = null,
@SerializedName("tagNames")
val tagNames: List<String>? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,21 @@ class MemoRepository @Inject constructor(

suspend fun updateMemo(
memoId: Long,
content: String
content: String? = null,
tagNames: List<String>? = null
): Response<MemoResponse> {
return memoApi.updateMemo(
memoId = memoId,
request = UpdateMemoRequest(content = content)
request = UpdateMemoRequest(
content = content,
tagNames = tagNames
)
)
}

suspend fun deleteMemo(
memoId: Long
): Response<Unit> {
return memoApi.deleteMemo(memoId = memoId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import com.example.hangsha_android.ui.view.interestpriority.InterestPriorityScre
import com.example.hangsha_android.ui.view.interestpriority.InterestPriorityViewModel
import com.example.hangsha_android.ui.view.mypage.MyPageScreen
import com.example.hangsha_android.ui.view.mypage.MyPageViewModel
import com.example.hangsha_android.ui.view.mymemos.MyMemosScreen
import com.example.hangsha_android.ui.view.mymemos.MyMemosViewModel
import com.example.hangsha_android.ui.view.serverhealth.ServerHealthViewModel
import com.example.hangsha_android.ui.view.signup.SignUpScreen
import com.example.hangsha_android.ui.view.signup.SignUpViewModel
Expand All @@ -64,6 +66,7 @@ sealed class HangshaDestinations(val route: String) {
data object Main : HangshaDestinations("main")
data object InterestPriority : HangshaDestinations("interest_priority")
data object MyBookmarks : HangshaDestinations("my_bookmarks")
data object MyMemos : HangshaDestinations("my_memos")
data object DailyEvents : HangshaDestinations("daily_events/{date}") {
const val baseRoute = "daily_events"
const val dateArg = "date"
Expand Down Expand Up @@ -444,6 +447,47 @@ fun NavGraphBuilder.mainGraph(navController: NavHostController) {
}
)
}
composable(HangshaDestinations.MyMemos.route) {
val myMemosViewModel: MyMemosViewModel = hiltViewModel()
val myMemosUiState by myMemosViewModel.uiState.collectAsState()
val context = LocalContext.current
val myMemosLifecycleOwner = LocalLifecycleOwner.current

LaunchedEffect(myMemosUiState.toastMessage) {
val message = myMemosUiState.toastMessage ?: return@LaunchedEffect
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
myMemosViewModel.onToastMessageConsumed()
}

DisposableEffect(myMemosLifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
myMemosViewModel.loadMemos()
}
}
myMemosLifecycleOwner.lifecycle.addObserver(observer)
onDispose {
myMemosLifecycleOwner.lifecycle.removeObserver(observer)
}
}

MyMemosScreen(
uiState = myMemosUiState,
onNavigateBack = { navController.popBackStack() },
onMemoClick = { eventId ->
navController.navigate(HangshaDestinations.EventDetail.createRoute(eventId))
},
onDeleteMemoClick = { memoId -> myMemosViewModel.deleteMemo(memoId) },
onStartEditMemo = { memo -> myMemosViewModel.startEditMemo(memo) },
onEditContentChanged = { value -> myMemosViewModel.onEditContentChanged(value) },
onStartAddingTag = { myMemosViewModel.startAddingTag() },
onEditTagInputChanged = { value -> myMemosViewModel.onEditTagInputChanged(value) },
onAddEditTag = { myMemosViewModel.addEditTag() },
onRemoveEditTag = { tagName -> myMemosViewModel.removeEditTag(tagName) },
onSaveEditedMemo = { myMemosViewModel.saveEditedMemo() },
onRetryClick = { myMemosViewModel.loadMemos() }
)
}
composable(HangshaDestinations.InterestPriority.route) {
val interestPriorityViewModel: InterestPriorityViewModel = hiltViewModel()
val interestPriorityUiState by interestPriorityViewModel.uiState.collectAsState()
Expand Down Expand Up @@ -549,7 +593,7 @@ fun NavGraphBuilder.mainGraph(navController: NavHostController) {
navController.navigate(HangshaDestinations.EventDetail.createRoute(eventId))
},
onMemoListClick = {
// TODO: Navigate to the memo detail/list page when that screen is implemented.
navController.navigate(HangshaDestinations.MyMemos.route)
},
onMemoEventClick = { eventId ->
navController.navigate(HangshaDestinations.EventDetail.createRoute(eventId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ fun EventDetailMemoSection(
)
}

// TODO: Connect tag creation/update API for existing memos when the backend contract is ready.
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ class EventDetailViewModel @Inject constructor(
val currentState = _uiState.value
val currentItem = currentState.item ?: return
val content = currentState.memoContent.trim()
if (content.isBlank()) {
val tagNames = (currentState.memoTagNames + currentState.memoTagInput.trim())
.filter { it.isNotBlank() }
.distinct()
if (currentState.savedMemo == null && content.isBlank()) {
_uiState.update {
it.copy(memoSaveMessage = "메모를 입력해주세요.")
}
Expand All @@ -158,33 +161,49 @@ class EventDetailViewModel @Inject constructor(
runCatching {
val savedMemo = currentState.savedMemo
if (savedMemo == null) {
val tagNames = (currentState.memoTagNames + currentState.memoTagInput.trim())
.filter { it.isNotBlank() }
.distinct()
memoRepository.createMemo(
eventId = currentItem.id,
content = content,
tagNames = tagNames
).requireBody("Memo response was empty.")
} else if (content.isBlank() && tagNames.isEmpty()) {
val response = memoRepository.deleteMemo(savedMemo.id)
if (!response.isSuccessful) {
throw HttpException(response)
}
null
} else {
memoRepository.updateMemo(
memoId = savedMemo.id,
content = content
content = content,
tagNames = tagNames
).requireBody("Memo response was empty.")
}
}.fold(
onSuccess = { memo ->
_uiState.update {
val savedMemo = memo.toEventDetailMemo()
it.copy(
isMemoEditorOpen = false,
memoContent = savedMemo.content,
memoTagInput = "",
memoTagNames = savedMemo.tagNames,
savedMemo = savedMemo,
isMemoSaving = false,
memoSaveMessage = "메모가 저장되었습니다."
)
if (memo == null) {
it.copy(
isMemoEditorOpen = false,
memoContent = "",
memoTagInput = "",
memoTagNames = emptyList(),
savedMemo = null,
isMemoSaving = false,
memoSaveMessage = "메모가 삭제되었습니다."
)
} else {
val savedMemo = memo.toEventDetailMemo()
it.copy(
isMemoEditorOpen = false,
memoContent = savedMemo.content,
memoTagInput = "",
memoTagNames = savedMemo.tagNames,
savedMemo = savedMemo,
isMemoSaving = false,
memoSaveMessage = "메모가 저장되었습니다."
)
}
}
},
onFailure = { error ->
Expand Down
Loading
Loading