tgyuuAn / BaekyoungE

자연어 처리 기반 진로 상담 chat bot 어플리케이션
5 stars 0 forks source link

[FEATURE]: 드디어 올 것이 왔다 멘토 멘티 채팅 with only 파이어베이스 #43

Closed tgyuuAn closed 2 weeks ago

tgyuuAn commented 1 month ago

작업 사항

image

자... 일단 태스크가 너무 크기 때문에... 쪼개보자..

image

image

image

image

image


Todo

기타사항

진짜 많네

tgyuuAn commented 1 month ago

뒤로가기로 돌아왔을 때 데이터가 갱신 시키고 싶다구요?

상황

저장소 화면에서 채팅 기록을 들어갔다가 채팅을 하나 치고 나온 뒤, 저장소 화면으로 돌아오면 새로운 데이터로 갱신되어야 하는데,

기존

backstack()으로 돌아왔기 때문에 새롭게 데이터를 호출하는 로직을 넣기 힘들었음.

// StorageScreen

@Composable
internal fun StorageRoute(
    navigateToChatting: (String) -> Unit,
    viewModel: StorageViewModel = hiltViewModel(),
) {
    val selectedYear by viewModel.selectedYear.collectAsStateWithLifecycle()
    val chattingRooms by viewModel.chattingLogs.collectAsStateWithLifecycle()
    val yearPickerState = rememberFWheelPickerState()
    val (showChatLogDeleteDialog, setChatLogDeleteDialog) = remember { mutableStateOf(false) }
    val snackbarHostState = remember { SnackbarHostState() }

    LaunchedEffect(true) {
        viewModel.eventFlow.collectLatest { event ->
            when (event) {
                is StorageViewModel.StorageEvent.DeleteSuccess -> setChatLogDeleteDialog(false)
                is StorageViewModel.StorageEvent.EventFailed ->
                    snackbarHostState.showSnackbar(event.message)
            }
        }
    }

    StorageScreen(
        snackbarHostState = snackbarHostState,
        selectedYear = selectedYear,
        chattingRooms = chattingRooms,
        yearPickerState = yearPickerState,
        showChatLogDeleteDialog = showChatLogDeleteDialog,
        setChatLogDeleteDialog = setChatLogDeleteDialog,
        deleteChattingRoom = viewModel::deleteChattingRoom,
        navigateToChatting = navigateToChatting,
    )
}

// StorageViewModel
Inject constructor(
    private val getAllChattingLogUseCase: GetAllChattingLogUseCase,
    private val deleteChattingRoomUseCase: DeleteChattingRoomUseCase,
) : ViewModel() {
    private val _eventFlow = MutableSharedFlow<StorageEvent>()
    val eventFlow = _eventFlow.asSharedFlow()

    private val _selectedYear = MutableStateFlow("2024")
    val selectedYear = _selectedYear.asStateFlow()

    private val _chattingLogs = MutableStateFlow<List<ChattingRoom>>(listOf())
    val chattingLogs = _chattingLogs.asStateFlow()

    init {
        getAllChattingLogs()
    }

    fun event(event: StorageEvent) = viewModelScope.launch { _eventFlow.emit(event) }

    private fun getAllChattingLogs() = viewModelScope.launch {
        getAllChattingLogUseCase()
            .onSuccess { _chattingLogs.value = it }
            .onFailure { event(StorageEvent.EventFailed(it.toString())) }
    }

즉, popBackStack()으로 돌아오더라도 ViewModel init 블럭에 있는 getAllChattingLogs() 를 호출해야 했음.







해결책

해결책 매우 간단했다.

ViewModel의 init 에서 호출하던 로직을 StorageRouet의 LaunchedEffect()에서 호출하도록 변경하면 되었음. 크크

@Composable
internal fun StorageRoute(
    navigateToChatting: (String) -> Unit,
    viewModel: StorageViewModel = hiltViewModel(),
) {
    val selectedYear by viewModel.selectedYear.collectAsStateWithLifecycle()
    val chattingRooms by viewModel.chattingLogs.collectAsStateWithLifecycle()
    val yearPickerState = rememberFWheelPickerState()
    val (showChatLogDeleteDialog, setChatLogDeleteDialog) = remember { mutableStateOf(false) }
    val snackbarHostState = remember { SnackbarHostState() }

    LaunchedEffect(true) {
        viewModel.getAllChattingLogs()

        viewModel.eventFlow.collectLatest { event ->
            when (event) {
                is StorageViewModel.StorageEvent.DeleteSuccess -> setChatLogDeleteDialog(false)
                is StorageViewModel.StorageEvent.EventFailed ->
                    snackbarHostState.showSnackbar(event.message)
            }
        }
    }

    StorageScreen(
        snackbarHostState = snackbarHostState,
        selectedYear = selectedYear,
        chattingRooms = chattingRooms,
        yearPickerState = yearPickerState,
        showChatLogDeleteDialog = showChatLogDeleteDialog,
        setChatLogDeleteDialog = setChatLogDeleteDialog,
        deleteChattingRoom = viewModel::deleteChattingRoom,
        navigateToChatting = navigateToChatting,
    )
}

그냥 궁금해서 찾아본... hiltViewModel()을 사용하면 생명주기가 어떻게 되는데요?

image

속하고있는 navigationGraph의 생명주기를 따라간다.




이 때, hiltViewModel() 말고도 아래와 같이 viewModel() 로도 뷰모델을 생성할 수 있는데,

@Composable
MainScreen(
  viewModel: MainViewModel = viewModel()
)

이렇게 될 경우 해당 MainScreen 이 호출될 때마다 ViewModel 인스턴스가 재생성된다.

반면, hiltViewModel()로 생성된 viewModel은 생명주기가 NavGraph와 같게 됌.

레퍼런스

tgyuuAn commented 1 month ago

ModalDrawerSheet는 기본값이 왼쪽 -> 오른쪽으로 열리는데 어떻게 오른쪽 -> 왼쪽으로 열리게 할래요?

이제 원래는 아래처럼 왼쪽 -> 오른쪽으로 열리는게 default인데,

image




이제 우리가 원하는 것은 카톡방 서랍처럼 아래 그림이걸랑요

image




이 때는, 아래 처럼 오른쪽에서 왼쪽으로 읽히듯이 제공하는 layoutDirection을 강제로 Rtl로 바꿔서 우회해서 구현가능....!

레퍼런스 1 레퍼런스 2

    CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
        ModalNavigationDrawer(
            drawerState = drawerState,
            drawerContent = {
                CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
                    Column(
                        modifier = Modifier
                            .background(BaekyoungTheme.colors.white)
                            .width((localConfiguration.screenWidthDp.dp * 3) / 4),
                    ) {
                        Text(
tgyuuAn commented 1 month ago

Android Modifier에 clicakble을 달면 ripple 효과 맘에 안드는데 없애고 싶어요

뒤에 ripple 나오는 거 너무 구려요 없애주세요.




clickable 함수는 두 가지 파라미터로 오버로딩이 되어있는데,

이 때 까지 주로 위 함수로 사용했지만,

클릭 시 ripple 효과를 제거 혹은 다른 효과로 변경 하고 싶을 경우 밑의 함수를 사용해야 한다.







아래와 같이 indiciationnull을 넣어주면 되는데, 이 때 아래 함수를 사용하기 위해서는 interactionSource가 필수적으로 들어가야하는 파라미터이기 때문에 아래처럼 해주면 끝!!!

modifier = Modifier.clickable(
    interactionSource = remember { MutableInteractionSource() },
    indication = null,
) {
    // Todo             
},

레퍼런스 1 레퍼런스 2

tgyuuAn commented 3 weeks ago

완전 대박 파이어베이스 채팅방 레퍼런스

사랑합니ㅏㄷ...