Skip to content
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

[Feature/#1049] Schedule 화면에 api를 연결합니다. #1050

Merged
merged 8 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ dependencies {
implementation(projects.data.mypage)
implementation(projects.data.poke)
implementation(projects.data.notification)
implementation(projects.data.schedule)
implementation(projects.core.common)
implementation(projects.core.analytics)
implementation(projects.core.network)
Expand Down
1 change: 1 addition & 0 deletions data/schedule/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
16 changes: 16 additions & 0 deletions data/schedule/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
sopt("feature")
}

android {
namespace = "org.sopt.official.data.schedule"
}

dependencies {
implementation(projects.domain.schedule)

implementation(projects.core.network)
implementation(projects.core.common)
implementation(platform(libs.okhttp.bom))
implementation(libs.bundles.okhttp)
}
Empty file.
4 changes: 4 additions & 0 deletions data/schedule/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.official.data.schedule.api

import org.sopt.official.data.schedule.dto.ScheduleResponse
import retrofit2.http.GET

interface ScheduleApi {
@GET("calendar/all")
suspend fun getSchedule(): List<ScheduleResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.sopt.official.data.schedule.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.official.common.di.AppRetrofit
import org.sopt.official.data.schedule.api.ScheduleApi
import retrofit2.Retrofit
import retrofit2.create
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object ApiModule {
@Provides
@Singleton
internal fun provideScheduleApi(@AppRetrofit(true) retrofit: Retrofit): ScheduleApi = retrofit.create()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.official.data.schedule.di

import org.sopt.official.domain.schedule.repository.ScheduleRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.official.data.schedule.repository.DefaultScheduleRepository
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal interface RepositoryModule {
@Binds
@Singleton
fun bindDefaultScheduleRepositoryRepository(defaultScheduleRepository: DefaultScheduleRepository): ScheduleRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.sopt.official.data.schedule.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ScheduleResponse(
@SerialName("date")
val date: String,
@SerialName("title")
val title: String,
@SerialName("type")
val type: String,
@SerialName("isRecentSchedule")
val isRecentSchedule: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.sopt.official.data.schedule.mapper

import org.sopt.official.domain.schedule.model.Schedule
import org.sopt.official.data.schedule.dto.ScheduleResponse

fun ScheduleResponse.toDomain() = Schedule(
date = date,
title = title,
type = type,
isRecentSchedule = isRecentSchedule
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.sopt.official.data.schedule.repository

import org.sopt.official.domain.schedule.model.Schedule
import org.sopt.official.domain.schedule.repository.ScheduleRepository
import org.sopt.official.data.schedule.api.ScheduleApi
import org.sopt.official.data.schedule.mapper.toDomain
import javax.inject.Inject

class DefaultScheduleRepository @Inject constructor(
private val scheduleApi: ScheduleApi,
) : ScheduleRepository {
override suspend fun getScheduleList(): Result<List<Schedule>> = runCatching {
scheduleApi.getSchedule().map { it.toDomain() }
}
}
1 change: 1 addition & 0 deletions domain/schedule/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
7 changes: 7 additions & 0 deletions domain/schedule/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
sopt("kotlin")
}

kotlin {
jvmToolchain(17)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.sopt.official.domain.schedule.model

data class Schedule(
val date: String,
val title: String,
val type: String,
val isRecentSchedule: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.official.domain.schedule.repository

import org.sopt.official.domain.schedule.model.Schedule

interface ScheduleRepository {
suspend fun getScheduleList(): Result<List<Schedule>>
}
3 changes: 3 additions & 0 deletions feature/schedule/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ dependencies {
// core
implementation(projects.core.common)
implementation(projects.core.designsystem)

// domain
implementation(projects.domain.schedule)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sopt.official.feature.schedule
package org.sopt.official.feature.schedule

import android.os.Bundle
import androidx.activity.compose.setContent
Expand All @@ -14,6 +14,8 @@ 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.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBackIosNew
Expand All @@ -24,44 +26,50 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sopt.official.feature.schedule.component.ScheduleItem
import com.sopt.official.feature.schedule.component.VerticalDividerWithCircle
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import org.sopt.official.designsystem.SoptTheme
import org.sopt.official.feature.schedule.component.ScheduleItem
import org.sopt.official.feature.schedule.component.VerticalDividerWithCircle

@AndroidEntryPoint
class ScheduleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
ScheduleScreen()
SoptTheme {
ScheduleScreen()
}
}
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScheduleScreen() {
val fakeData = listOf(
Pair("9월 28일 토요일", "1차 세미나"),
Pair("9월 28일 토요일", "2차 세미나"),
Pair("9월 28일 토요일", "3차 세미나"),
Pair("9월 28일 토요일", "4차 세미나"),
Pair("9월 28일 토요일", "5차 세미나"),
Pair("9월 28일 토요일", "6차 세미나"),
Pair("9월 28일 토요일", "7차 세미나"),
Pair("9월 28일 토요일", "8차 세미나"),
Pair("9월 28일 토요일", "9차 세미나"),
Pair("9월 28일 토요일", "10차 세미나"),
)
fun ScheduleScreen(
viewModel: ScheduleViewModel = hiltViewModel(),
) {
val lazyListState = rememberLazyListState()
val state by viewModel.schedule.collectAsStateWithLifecycle()

LaunchedEffect(state) {
if (state.scheduleList.isNotEmpty()) {
delay(200L)
lazyListState.animateScrollToItem(state.scheduleList.indexOfFirst { it.isRecentSchedule })
}
}

Scaffold(
topBar = {
Expand Down Expand Up @@ -98,17 +106,23 @@ fun ScheduleScreen() {
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 20.dp),
contentPadding = PaddingValues(vertical = 16.dp)
contentPadding = PaddingValues(vertical = 16.dp),
state = lazyListState
) {
items(fakeData) { item ->
items(state.scheduleList) { item ->
ScheduleItem(
date = item.first,
event = item.second
date = item.date,
title = item.title,
type = item.type,
isRecentSchedule = item.isRecentSchedule,
)
}

item {
VerticalDividerWithCircle(Color.Unspecified)
VerticalDividerWithCircle(
circleColor = Color.Unspecified,
height = 200.dp
)
}
Comment on lines 121 to 126
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이걸 추가한 이유가 있을까요?

}

Expand Down Expand Up @@ -155,7 +169,7 @@ fun ScheduleScreen() {

@Preview
@Composable
fun ScheduleActivityPreview() {
private fun ScheduleActivityPreview() {
SoptTheme {
ScheduleScreen()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.sopt.official.feature.schedule

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.sopt.official.domain.schedule.model.Schedule
import org.sopt.official.domain.schedule.repository.ScheduleRepository
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class ScheduleViewModel @Inject constructor(
private val scheduleRepository: ScheduleRepository,
) : ViewModel() {
private val _schedule = MutableStateFlow(ScheduleState())
val schedule: StateFlow<ScheduleState>
get() = _schedule.asStateFlow()

init {
getScheduleList()
}

private fun getScheduleList() {
viewModelScope.launch {
_schedule.update {
it.copy(isLoading = true)
}
scheduleRepository.getScheduleList().onSuccess { scheduleList ->
_schedule.update {
it.copy(
scheduleList = scheduleList.toPersistentList(),
isLoading = false,
)
}
}.onFailure { error ->
Timber.e(error)
_schedule.update {
it.copy(
isLoading = false,
isError = true,
)
}
}
}
}
}

data class ScheduleState(
val scheduleList: ImmutableList<Schedule> = persistentListOf(),
val isLoading: Boolean = false,
val isError: Boolean = false,
)
Loading