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] 시간표 마이그레이션 #291

Draft
wants to merge 29 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b26fe47
[ADD] init timetable compose setting
Jokwanhee Jun 14, 2024
8aca5ac
[ADD] timetable content header & get semester api
Jokwanhee Jun 14, 2024
1d6e367
[ADD] timetable UI & departments and lectures api
Jokwanhee Jun 14, 2024
f665100
[ADD] timetable bottomsheet
Jokwanhee Jun 14, 2024
0982dfe
[ADD] anoymous timetables
Jokwanhee Jun 15, 2024
b3874fe
[MOD] anonymous logic in datasource
Jokwanhee Jun 15, 2024
09d2f95
[ADD] user timetables
Jokwanhee Jun 15, 2024
ccbaff5
[MOD] bottomsheet roundedshape
Jokwanhee Jun 15, 2024
6ae63ab
[ADD] lecture color palette
Jokwanhee Jun 15, 2024
6cde7e3
[MOD] userState in timetable navi trail
Jokwanhee Jun 15, 2024
fb584aa
[MOD] user add timetables
Jokwanhee Jun 16, 2024
431b509
[UI] timetable
Jokwanhee Jun 16, 2024
5a793cb
[MOD] bottomsheetshape padding position & darkmode false
Jokwanhee Jun 16, 2024
19fd67c
[ADD] unregister listener solve memory leak
Jokwanhee Jun 16, 2024
8c72345
[MOD] bottomsheet height with ime
Jokwanhee Jun 16, 2024
a082078
[ADD] glance project settings
Jokwanhee Jun 19, 2024
035d9f5
[ADD] update widget
Jokwanhee Jun 20, 2024
46ab5de
[ADD] glance widget
Jokwanhee Jun 20, 2024
f5892d4
Merge branch 'develop' into feature/timetable_v2
Jokwanhee Jun 20, 2024
2580677
[MOD] timeblock ui
Jokwanhee Jun 21, 2024
4cc5c95
[MOD] timetable ui
Jokwanhee Jun 21, 2024
55d3040
[DEL] dragon image
Jokwanhee Jun 22, 2024
8d5a906
[ADD] lecture default colors
Jokwanhee Jun 22, 2024
8e77bda
[MOD] timetable widget label & invisible before timetable widget
Jokwanhee Jun 22, 2024
ddcbf45
[MOD] lecture description
Jokwanhee Jun 22, 2024
b61a7cd
[MOD] timetable header size
Jokwanhee Jun 25, 2024
a65a88a
[MOD] timetable widget ui
Jokwanhee Jun 25, 2024
079002a
[ADD] empty event time click enabled false
Jokwanhee Jun 26, 2024
ee9437e
[ADD] lecture click event trigger
Jokwanhee Jun 27, 2024
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
2 changes: 2 additions & 0 deletions data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ dependencies {

testImplementation libs.junit
androidTestImplementation libs.ext.junit

implementation(libs.androidx.datastore)
}
52 changes: 52 additions & 0 deletions data/src/main/assets/department.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"departments": [
{
"id": 1,
"name": "컴퓨터공학부"
},
{
"id": 2,
"name": "기계공학부"
},
{
"id": 3,
"name": "전기전자통신공학부"
},
{
"id": 4,
"name": "에너지신소재화학공학부"
},
{
"id": 5,
"name": "산업경영학부"
},
{
"id": 6,
"name": "메카트로닉스공학부"
},
{
"id": 7,
"name": "디자인건축공학부"
},
{
"id": 8,
"name": "고용서비스정책학과"
},
{
"id": 9,
"name": "안전공학과"
},
{
"id": 10,
"name": "교양학부"
},
{
"id": 11,
"name": "HRD학과"
},
{
"id": 12,
"name": "융합학과"
}
]
}
18 changes: 18 additions & 0 deletions data/src/main/java/in/koreatech/koin/data/api/TimetableApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package `in`.koreatech.koin.data.api

import `in`.koreatech.koin.data.constant.URLConstant.LECTURE
import `in`.koreatech.koin.data.constant.URLConstant.SEMESTERS
import `in`.koreatech.koin.data.response.timetable.LectureResponse
import `in`.koreatech.koin.data.response.timetable.SemesterResponse
import retrofit2.http.GET
import retrofit2.http.Query

interface TimetableApi {
@GET(SEMESTERS)
suspend fun getSemesters(): List<SemesterResponse>

@GET(LECTURE)
suspend fun getLectures(
@Query("semester_date") semester: String
): List<LectureResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package `in`.koreatech.koin.data.api.auth

import `in`.koreatech.koin.data.constant.URLConstant.TIMETABLE
import `in`.koreatech.koin.data.constant.URLConstant.TIMETABLES
import `in`.koreatech.koin.data.request.timetable.TimetablesRequest
import `in`.koreatech.koin.data.response.timetable.TimetablesResponse
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query

interface TimetableAuthApi {
@GET(TIMETABLES)
suspend fun getTimetables(
@Query("semester") semester: String,
): TimetablesResponse

@POST(TIMETABLES)
suspend fun postTimetables(
@Body timetables: TimetablesRequest
)

@DELETE(TIMETABLE)
suspend fun deleteTimetables(
@Query("id") id: Int
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package `in`.koreatech.koin.data.di.datastore

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.preferencesDataStoreFile
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton

private const val KOIN_PREFERENCES = "koin_preferences"

@InstallIn(SingletonComponent::class)
@Module
object DataStoreModule {
@Singleton
@Provides
fun providesPreferencesDataStore(@ApplicationContext appContext: Context): DataStore<Preferences> {
return PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler(
produceNewData = { emptyPreferences() }
),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
produceFile = { appContext.preferencesDataStoreFile(KOIN_PREFERENCES) }
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,12 @@ object NoAuthNetworkModule {
): LandApi {
return retrofit.create(LandApi::class.java)
}

@Provides
@Singleton
fun providesTimetableApi(
@NoAuth retrofit: Retrofit
): TimetableApi {
return retrofit.create(TimetableApi::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,58 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import `in`.koreatech.koin.data.repository.*
import `in`.koreatech.koin.data.source.local.*
import `in`.koreatech.koin.data.source.remote.*
import `in`.koreatech.koin.domain.repository.*
import `in`.koreatech.koin.data.repository.BusRepositoryImpl
import `in`.koreatech.koin.data.repository.DeptRepositoryImpl
import `in`.koreatech.koin.data.repository.DiningRepositoryImpl
import `in`.koreatech.koin.data.repository.LandRepositoryImpl
import `in`.koreatech.koin.data.repository.NotificationRepositoryImpl
import `in`.koreatech.koin.data.repository.OwnerChangePasswordRepositoryImpl
import `in`.koreatech.koin.data.repository.OwnerRegisterRepositoryImpl
import `in`.koreatech.koin.data.repository.OwnerSignupRepositoryImpl
import `in`.koreatech.koin.data.repository.OwnerVerificationCodeRepositoryImpl
import `in`.koreatech.koin.data.repository.PreSignedUrlRepositoryImpl
import `in`.koreatech.koin.data.repository.SignupRepositoryImpl
import `in`.koreatech.koin.data.repository.StoreRepositoryImpl
import `in`.koreatech.koin.data.repository.TimetableRepositoryImpl
import `in`.koreatech.koin.data.repository.TokenRepositoryImpl
import `in`.koreatech.koin.data.repository.UploadUrlRepositoryImpl
import `in`.koreatech.koin.data.repository.UserRepositoryImpl
import `in`.koreatech.koin.data.repository.VersionRepositoryImpl
import `in`.koreatech.koin.data.source.local.BusLocalDataSource
import `in`.koreatech.koin.data.source.local.DeptLocalDataSource
import `in`.koreatech.koin.data.source.local.SignupTermsLocalDataSource
import `in`.koreatech.koin.data.source.local.TimetableLocalDataSource
import `in`.koreatech.koin.data.source.local.TokenLocalDataSource
import `in`.koreatech.koin.data.source.local.VersionLocalDataSource
import `in`.koreatech.koin.data.source.remote.BusRemoteDataSource
import `in`.koreatech.koin.data.source.remote.DeptRemoteDataSource
import `in`.koreatech.koin.data.source.remote.DiningRemoteDataSource
import `in`.koreatech.koin.data.source.remote.LandRemoteDataSource
import `in`.koreatech.koin.data.source.remote.NotificationRemoteDataSource
import `in`.koreatech.koin.data.source.remote.OwnerRemoteDataSource
import `in`.koreatech.koin.data.source.remote.PreSignedUrlRemoteDataSource
import `in`.koreatech.koin.data.source.remote.StoreRemoteDataSource
import `in`.koreatech.koin.data.source.remote.TimetableRemoteDataSource
import `in`.koreatech.koin.data.source.remote.UploadUrlRemoteDataSource
import `in`.koreatech.koin.data.source.remote.UserRemoteDataSource
import `in`.koreatech.koin.data.source.remote.VersionRemoteDataSource
import `in`.koreatech.koin.domain.repository.BusRepository
import `in`.koreatech.koin.domain.repository.DeptRepository
import `in`.koreatech.koin.domain.repository.DiningRepository
import `in`.koreatech.koin.domain.repository.LandRepository
import `in`.koreatech.koin.domain.repository.NotificationRepository
import `in`.koreatech.koin.domain.repository.OwnerChangePasswordRepository
import `in`.koreatech.koin.domain.repository.OwnerRegisterRepository
import `in`.koreatech.koin.domain.repository.OwnerSignupRepository
import `in`.koreatech.koin.domain.repository.OwnerVerificationCodeRepository
import `in`.koreatech.koin.domain.repository.PreSignedUrlRepository
import `in`.koreatech.koin.domain.repository.SignupRepository
import `in`.koreatech.koin.domain.repository.StoreRepository
import `in`.koreatech.koin.domain.repository.TimetableRepository
import `in`.koreatech.koin.domain.repository.TokenRepository
import `in`.koreatech.koin.domain.repository.UploadUrlRepository
import `in`.koreatech.koin.domain.repository.UserRepository
import `in`.koreatech.koin.domain.repository.VersionRepository
import javax.inject.Singleton

@Module
Expand Down Expand Up @@ -154,4 +202,14 @@ object RepositoryModule {
): OwnerChangePasswordRepository {
return OwnerChangePasswordRepositoryImpl(ownerRemoteDataSource)
}

@Provides
@Singleton
fun providesTimetableRepository(
timetableRemoteDataSource: TimetableRemoteDataSource,
timetableLocalDataSource: TimetableLocalDataSource,
tokenLocalDataSource: TokenLocalDataSource
): TimetableRepository {
return TimetableRepositoryImpl(timetableRemoteDataSource, timetableLocalDataSource, tokenLocalDataSource)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package `in`.koreatech.koin.data.di.source

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -10,6 +12,7 @@ import `in`.koreatech.koin.core.qualifier.IoDispatcher
import `in`.koreatech.koin.data.source.local.BusLocalDataSource
import `in`.koreatech.koin.data.source.local.DeptLocalDataSource
import `in`.koreatech.koin.data.source.local.SignupTermsLocalDataSource
import `in`.koreatech.koin.data.source.local.TimetableLocalDataSource
import `in`.koreatech.koin.data.source.local.TokenLocalDataSource
import `in`.koreatech.koin.data.source.local.VersionLocalDataSource
import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -58,4 +61,13 @@ object LocalDataSourceModule {
) : DeptLocalDataSource {
return DeptLocalDataSource(applicationContext)
}

@Provides
@Singleton
fun providesTimetableLocalDataSource(
@ApplicationContext applicationContext: Context,
dataStore: DataStore<Preferences>
): TimetableLocalDataSource {
return TimetableLocalDataSource(applicationContext, dataStore)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import `in`.koreatech.koin.data.api.*
import `in`.koreatech.koin.data.api.auth.TimetableAuthApi
import `in`.koreatech.koin.data.api.auth.UserAuthApi
import `in`.koreatech.koin.data.source.remote.*
import javax.inject.Singleton
Expand Down Expand Up @@ -100,4 +101,13 @@ object RemoteDataSourceModule {
): PreSignedUrlRemoteDataSource {
return PreSignedUrlRemoteDataSource(preSignedUrlApi)
}

@Provides
@Singleton
fun providesTimetableRemoteDataSource(
timetableApi: TimetableApi,
timetableAuthApi: TimetableAuthApi
): TimetableRemoteDataSource {
return TimetableRemoteDataSource(timetableApi, timetableAuthApi)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package `in`.koreatech.koin.data.mapper

import com.google.gson.annotations.SerializedName
import `in`.koreatech.koin.data.request.timetable.TimetablesLectureRequest
import `in`.koreatech.koin.data.request.timetable.TimetablesRequest
import `in`.koreatech.koin.data.response.timetable.DepartmentResponse
import `in`.koreatech.koin.data.response.timetable.LectureResponse
import `in`.koreatech.koin.data.response.timetable.SemesterResponse
import `in`.koreatech.koin.data.response.timetable.TimetablesLectureResponse
import `in`.koreatech.koin.domain.model.timetable.Department
import `in`.koreatech.koin.domain.model.timetable.Lecture
import `in`.koreatech.koin.domain.model.timetable.Semester

fun SemesterResponse.toSemester() = Semester(
id = this.id,
semester = this.semester
)

fun LectureResponse.toLecture() = Lecture(
id = this.id ?: 0,
code = this.code ?: "",
name = this.name ?: "",
grades = this.grades ?: "",
lectureClass = this.lectureClass ?: "",
regularNumber = this.regularNumber ?: "",
department = this.department ?: "",
target = this.target ?: "",
professor = this.professor ?: "",
isEnglish = this.isEnglish ?: "",
designScore = this.designScore ?: "",
isElearning = this.isElearning ?: "",
classTime = this.classTime,
)

fun TimetablesLectureResponse.toLecture() = Lecture(
id = this.id ?: 0,
code = this.code ?: "",
name = this.name ?: "",
grades = this.grades ?: "",
lectureClass = this.lectureClass ?: "",
regularNumber = this.regularNumber ?: "",
department = this.department ?: "",
target = this.target ?: "",
professor = this.professor ?: "",
isEnglish = "",
designScore = this.designScore ?: "",
isElearning = "",
classTime = this.classTime.orEmpty(),
)
Comment on lines +19 to +49
Copy link
Contributor

Choose a reason for hiding this comment

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

lecture에 대해서 두개의 response를 둔 이유가 궁금합니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이건 서버에서 불러오는 시간표 api response 스키마가 동일하지 않아서 response dto 를 두개 만들어 놨습니다.. 서버에게 말해야하지만 이번에 v2로 api를 만들고 있는걸로 알아서 공수가 또 들어갈것같아 우선 냅뒀습니다.. 추가로 이전 버전의 사이드 이펙트나 안드로이드가 아닌 다른 플랫폼에서 발생하는 이슈를 생각해보니 하암..

/lectures (강의 목록 조회)
/timetables (시간표 정보 조회)


fun DepartmentResponse.toDepartment() = Department(
id = this.id,
name = this.name
)

fun List<Lecture>.toTimetablesRequest(semester: String) = TimetablesRequest(
timetable = this.map { it.toTimetablesLectureResponse() },
semester = semester
)

fun Lecture.toTimetablesLectureResponse() = TimetablesLectureRequest(
id = this.id,
code = this.code,
name = this.name,
grades = this.grades,
lectureClass = this.lectureClass,
regularNumber = this.regularNumber,
department = this.department,
target = this.target,
professor = this.professor,
designScore = this.designScore,
classPlace = "",
memo = "",
classTime = this.classTime
)
Loading