Skip to content

Commit

Permalink
[MERGE] #91 -> #59
Browse files Browse the repository at this point in the history
[FEAT/#91] ์บ˜๋ฆฐ๋”๋ทฐ / ์Šคํฌ๋žฉ ๋ชฉ๋ก ์„œ๋ฒ„ํ†ต์‹ 
boiledEgg-s authored Jul 17, 2024
2 parents 12641e9 + 76093d1 commit 3f9a40d
Showing 84 changed files with 2,184 additions and 961 deletions.
21 changes: 13 additions & 8 deletions app/src/main/java/com/terning/point/di/DataSourceModule.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.terning.point.di

import com.terning.data.datasource.MockDataSource
import com.terning.data.datasource.ScrapDataSource
import com.terning.data.datasourceimpl.MockDataSourceImpl
import com.terning.data.datasourceimpl.ScrapDataSourceImpl
import com.terning.data.repositoryimpl.ScrapRepositoryImpl
import com.terning.domain.repository.ScrapRepository
import com.terning.data.datasource.AuthDataSource
import com.terning.data.datasource.SearchDataSource
import com.terning.data.datasourceimpl.AuthDataSourceImpl
import com.terning.data.datasourceimpl.SearchDataSourceImpl
import com.terning.data.datasource.CalendarDataSource
import com.terning.data.datasourceimpl.CalendarDataSourceImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@@ -18,9 +18,14 @@ abstract class DataSourceModule {

@Binds
@Singleton
abstract fun bindScrapRepository(scrapDataSourceImpl: ScrapDataSourceImpl): ScrapDataSource
abstract fun bindCalendarRepository(calendarDataSourceImpl: CalendarDataSourceImpl): CalendarDataSource

@Binds
@Singleton
abstract fun bindMockDataSource(mockDataSourceImpl: MockDataSourceImpl): MockDataSource
abstract fun bindAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource

@Binds
@Singleton
abstract fun bindSearchViewsDataSource(searchViewsDataSourceImpl: SearchDataSourceImpl):
SearchDataSource
}
27 changes: 27 additions & 0 deletions app/src/main/java/com/terning/point/di/DataStoreModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.terning.point.di

import android.content.Context
import android.content.SharedPreferences
import com.terning.data.local.TerningDataStore
import com.terning.data.local.TerningDataStoreImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {

@Provides
@Singleton
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)

@Provides
@Singleton
fun provideTerningDataStore(dataStoreImpl: TerningDataStoreImpl): TerningDataStore =
dataStoreImpl
}
24 changes: 18 additions & 6 deletions app/src/main/java/com/terning/point/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.terning.point.di

import com.terning.data.repositoryimpl.MockRepositoryImpl
import com.terning.data.repositoryimpl.ScrapRepositoryImpl
import com.terning.domain.repository.MockRepository
import com.terning.domain.repository.ScrapRepository
import com.terning.data.repositoryimpl.AuthRepositoryImpl
import com.terning.data.repositoryimpl.CalendarRepositoryImpl
import com.terning.data.repositoryimpl.SearchViewsRepositoryImpl
import com.terning.data.repositoryimpl.TokenRepositoryImpl
import com.terning.domain.repository.AuthRepository
import com.terning.domain.repository.CalendarRepository
import com.terning.domain.repository.SearchRepository
import com.terning.domain.repository.TokenRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@@ -16,9 +20,17 @@ abstract class RepositoryModule {

@Binds
@Singleton
abstract fun scrapRepository(scrapRepositoryImpl: ScrapRepositoryImpl): ScrapRepository
abstract fun calendarRepository(calendarRepositoryImpl: CalendarRepositoryImpl): CalendarRepository

@Binds
@Singleton
abstract fun bindMockRepository(mockRepositoryImpl: MockRepositoryImpl): MockRepository
abstract fun bindAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository

@Binds
@Singleton
abstract fun bindTokenRepository(tokenRepositoryImpl: TokenRepositoryImpl): TokenRepository

@Binds
@Singleton
abstract fun bindSearchViewsRepository(searchViewsRepositoryImpl: SearchViewsRepositoryImpl): SearchRepository
}
10 changes: 5 additions & 5 deletions app/src/main/java/com/terning/point/di/RetrofitModule.kt
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@ package com.terning.point.di

import com.terning.core.extension.isJsonArray
import com.terning.core.extension.isJsonObject
import com.terning.point.BuildConfig
import com.terning.point.di.qualifier.OPEN
import com.terning.point.BuildConfig.BASE_URL
import com.terning.point.di.qualifier.JWT
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -65,12 +65,12 @@ object RetrofitModule {

@Provides
@Singleton
@OPEN
fun provideOpenRetrofit(
@JWT
fun provideJWTRetrofit(
client: OkHttpClient,
factory: Converter.Factory
): Retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.baseUrl(BASE_URL)
.addConverterFactory(factory)
.client(client)
.build()
20 changes: 16 additions & 4 deletions app/src/main/java/com/terning/point/di/ServiceModule.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.terning.point.di

import com.terning.data.service.MockService
import com.terning.point.di.qualifier.OPEN
import com.terning.data.service.AuthService
import com.terning.data.service.CalendarService
import com.terning.data.service.SearchService
import com.terning.point.di.qualifier.JWT
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -15,7 +17,17 @@ object ServiceModule {

@Provides
@Singleton
fun provideMockService(@OPEN retrofit: Retrofit): MockService =
retrofit.create(MockService::class.java)
fun provideAuthService(@JWT retrofit: Retrofit): AuthService =
retrofit.create(AuthService::class.java)

@Provides
@Singleton
fun provideSearchService(@JWT retrofit: Retrofit): SearchService =
retrofit.create(SearchService::class.java)

@Provides
@Singleton
fun provideCalendarService(@JWT retrofit: Retrofit): CalendarService =
retrofit.create(CalendarService::class.java)

}
Original file line number Diff line number Diff line change
@@ -5,4 +5,4 @@ import javax.inject.Qualifier

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OPEN
annotation class JWT
Original file line number Diff line number Diff line change
@@ -68,9 +68,13 @@ fun TerningBasicTextField(
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {
keyboardController?.hide()
focusManager.clearFocus()
onDoneAction?.invoke()
if (value.isNotBlank()) {
keyboardController?.hide()
focusManager.clearFocus()
if (onDoneAction != null) {
onDoneAction()
}
}
}
),

6 changes: 6 additions & 0 deletions core/src/main/java/com/terning/core/extension/LocalDateExt.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.terning.core.extension

import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.format.TextStyle
import java.util.Locale

@@ -10,6 +11,11 @@ fun LocalDate.getStringAsTitle(): String =
fun LocalDate.getDateStringInKorean(): String =
"${monthValue}์›” ${dayOfMonth}์ผ ${dayOfWeek.getDisplayName(TextStyle.FULL, Locale.KOREAN)}"

fun LocalDate.getDateAsMapString(): String {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
return format(formatter)
}

fun LocalDate.getWeekIndexContainingSelectedDate(inDays: Int): Int = (inDays + dayOfMonth - 1) / 7

fun LocalDate.isToday(): Boolean = this == LocalDate.now()
3 changes: 3 additions & 0 deletions core/src/main/java/com/terning/core/extension/Map.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.terning.core.extension

fun<T> List<T>?.isListNotEmpty():Boolean = this.orEmpty().isNotEmpty()
12 changes: 12 additions & 0 deletions data/src/main/java/com/terning/data/datasource/AuthDataSource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.terning.data.datasource

import com.terning.data.dto.BaseResponse
import com.terning.data.dto.request.SignInRequestDto
import com.terning.data.dto.response.SignInResponseDto

interface AuthDataSource {
suspend fun postSignIn(
authorization: String,
platform: SignInRequestDto
): BaseResponse<SignInResponseDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.terning.data.datasource

import com.terning.data.dto.BaseResponse
import com.terning.data.dto.request.CalendarDayListRequestDto
import com.terning.data.dto.request.CalendarMonthListRequestDto
import com.terning.data.dto.request.CalendarMonthRequestDto
import com.terning.data.dto.response.CalendarDayListResponseDto
import com.terning.data.dto.response.CalendarMonthListResponseDto
import com.terning.data.dto.response.CalendarMonthResponseDto

interface CalendarDataSource {
suspend fun getCalendarMonth(request: CalendarMonthRequestDto): BaseResponse<List<CalendarMonthResponseDto>>
suspend fun getCalendarMonthList(request: CalendarMonthListRequestDto): BaseResponse<List<CalendarMonthListResponseDto>>
suspend fun getCalendarDayList(request: CalendarDayListRequestDto): BaseResponse<List<CalendarDayListResponseDto>>
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.data.datasource

import com.terning.data.dto.BaseResponse
import com.terning.data.dto.response.SearchViewsResponseDto

interface SearchDataSource {
suspend fun getSearchViews(): BaseResponse<SearchViewsResponseDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.terning.data.datasourceimpl

import com.terning.data.datasource.AuthDataSource
import com.terning.data.dto.BaseResponse
import com.terning.data.dto.request.SignInRequestDto
import com.terning.data.dto.response.SignInResponseDto
import com.terning.data.service.AuthService
import javax.inject.Inject

class AuthDataSourceImpl @Inject constructor(
private val authService: AuthService
) : AuthDataSource {
override suspend fun postSignIn(
authorization: String,
platform: SignInRequestDto
): BaseResponse<SignInResponseDto> = authService.postSignIn(authorization, platform)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.terning.data.datasourceimpl

import com.terning.data.datasource.CalendarDataSource
import com.terning.data.dto.BaseResponse
import com.terning.data.dto.request.CalendarDayListRequestDto
import com.terning.data.dto.request.CalendarMonthListRequestDto
import com.terning.data.dto.request.CalendarMonthRequestDto
import com.terning.data.dto.response.CalendarDayListResponseDto
import com.terning.data.dto.response.CalendarMonthListResponseDto
import com.terning.data.dto.response.CalendarMonthResponseDto
import com.terning.data.service.CalendarService
import javax.inject.Inject

class CalendarDataSourceImpl @Inject constructor(
private val calendarService: CalendarService
) : CalendarDataSource {
override suspend fun getCalendarMonth(request: CalendarMonthRequestDto): BaseResponse<List<CalendarMonthResponseDto>> =
CalendarList.getCalendarScrapMonth(request)

override suspend fun getCalendarMonthList(request: CalendarMonthListRequestDto): BaseResponse<List<CalendarMonthListResponseDto>> =
CalendarList.getCalendarScrapMonthList(request)

override suspend fun getCalendarDayList(request: CalendarDayListRequestDto): BaseResponse<List<CalendarDayListResponseDto>> =
CalendarList.getCalendarScrapDayList(request)
}
846 changes: 846 additions & 0 deletions data/src/main/java/com/terning/data/datasourceimpl/CalendarList.kt

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.terning.data.datasourceimpl

import com.terning.data.datasource.SearchDataSource
import com.terning.data.dto.BaseResponse
import com.terning.data.dto.response.SearchViewsResponseDto
import com.terning.data.service.SearchService
import javax.inject.Inject

class SearchDataSourceImpl @Inject constructor(
private val searchService: SearchService,
) : SearchDataSource {
override suspend fun getSearchViews(): BaseResponse<SearchViewsResponseDto> =
searchService.getSearchViewsList()
}
Original file line number Diff line number Diff line change
@@ -7,8 +7,6 @@ import kotlinx.serialization.Serializable
data class NonDataBaseResponse(
@SerialName("status")
val status: Int,
@SerialName("code")
val code: String,
@SerialName("message")
val message: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.terning.data.dto.request

data class CalendarDayListRequestDto(
val date: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.terning.data.dto.request

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

@Serializable
data class CalendarMonthListRequestDto(
@SerialName("deadline")
val year: Int,
@SerialName("scraps")
val month: Int
)

Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ScrapRequestDto(
data class CalendarMonthRequestDto(
@SerialName("year")
val year: Int,
@SerialName("month")
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.terning.data.dto.request

import com.terning.domain.entity.request.SignInRequestModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SignInRequestDto(
@SerialName("authType")
val authType: String
)

fun SignInRequestModel.toSignInRequestDto(): SignInRequestDto =
SignInRequestDto(authType = authType)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.terning.data.dto.response

import com.terning.domain.entity.response.CalendarScrapDetailModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CalendarDayListResponseDto(
@SerialName("scrapId")
val scrapId: Long,
@SerialName("internshipAnnouncementId")
val internshipAnnouncementId: Long,
@SerialName("title")
val title: String,
@SerialName("dDay")
val dDay: String,
@SerialName("workingPeriod")
val workingPeriod: String,
@SerialName("color")
val color: String,
@SerialName("companyImage")
val companyImage: String,
@SerialName("startYear")
val startYear: Int,
@SerialName("startMonth")
val startMonth: Int
){
fun toScrapDetailModelList(): CalendarScrapDetailModel =
CalendarScrapDetailModel(
scrapId = scrapId,
internshipAnnouncementId = internshipAnnouncementId,
title = title,
dDay = dDay,
workingPeriod = workingPeriod,
color = color,
companyImage = companyImage,
startYear = startYear,
startMonth = startMonth,
deadLine = ""
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.terning.data.dto.response

import com.terning.domain.entity.response.CalendarScrapDetailModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CalendarMonthListResponseDto(
@SerialName("deadline")
val deadline: String,
@SerialName("scraps")
val scraps: List<Scrap>
) {
@Serializable
data class Scrap(
@SerialName("scrapId")
val scrapId: Long,
@SerialName("internshipAnnouncementId")
val internshipAnnouncementId: Long,
@SerialName("title")
val title: String,
@SerialName("dDay")
val dDay: String,
@SerialName("workingPeriod")
val workingPeriod: String,
@SerialName("color")
val color: String,
@SerialName("companyImage")
val companyImage: String,
@SerialName("startYear")
val startYear: Int,
@SerialName("startMonth")
val startMonth: Int
)

fun toScrapDetailModelList(): List<CalendarScrapDetailModel> = scraps.map { scrap ->
CalendarScrapDetailModel(
scrapId = scrap.scrapId,
internshipAnnouncementId = scrap.internshipAnnouncementId,
title = scrap.title,
dDay = scrap.dDay,
workingPeriod = scrap.workingPeriod,
color = scrap.color,
companyImage = scrap.companyImage,
startYear = scrap.startYear,
startMonth = scrap.startMonth,
deadLine = deadline
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.terning.data.dto.response

import com.terning.domain.entity.response.CalendarScrapModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CalendarMonthResponseDto(
@SerialName("deadline")
val deadline: String,
@SerialName("scraps")
val scraps: List<Scrap>
) {
@Serializable
data class Scrap(
@SerialName("scrapId")
val scrapId: Long,
@SerialName("title")
val title: String,
@SerialName("color")
val color: String
)

fun toScrapModelList(): List<CalendarScrapModel> = scraps.map { scrap ->
CalendarScrapModel(
scrapId = scrap.scrapId,
title = scrap.title,
color = scrap.color,
deadLine = deadline,
isScrapped = true
)
}
}

This file was deleted.

328 changes: 0 additions & 328 deletions data/src/main/java/com/terning/data/dto/response/ScrapResponseDto.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.terning.data.dto.response

import com.terning.domain.entity.response.InternshipAnnouncement
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SearchViewsResponseDto(
@SerialName("internshipAnnouncementId")
val internshipAnnouncementId: Long,
@SerialName("companyImage")
val companyImage: String,
@SerialName("title")
val title: String,
) {


fun toSearchViewsEntity(): List<InternshipAnnouncement> {
return listOf(
InternshipAnnouncement(
announcementId = internshipAnnouncementId,
companyImage = companyImage,
title = title,
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.terning.data.dto.response

import com.terning.domain.entity.response.SignInResponseModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SignInResponseDto(
@SerialName("accessToken")
val accessToken: String,
@SerialName("refreshToken")
val refreshToken: String,
@SerialName("userId")
val userId: Long,
@SerialName("authType")
val authType: String,
) {
fun toSignInModel() = SignInResponseModel(
accessToken = accessToken,
refreshToken = refreshToken,
userId = userId,
authType = authType
)
}
8 changes: 8 additions & 0 deletions data/src/main/java/com/terning/data/local/TerningDataStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.data.local

interface TerningDataStore {
var accessToken: String
var refreshToken: String
var userId: Long
fun clearInfo()
}
31 changes: 31 additions & 0 deletions data/src/main/java/com/terning/data/local/TerningDataStoreImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.terning.data.local

import android.content.SharedPreferences
import androidx.core.content.edit
import javax.inject.Inject

class TerningDataStoreImpl @Inject constructor(
private val dataStore: SharedPreferences,
) : TerningDataStore {
override var accessToken: String
get() = dataStore.getString(ACCESS_TOKEN, "") ?: ""
set(value) = dataStore.edit { putString(ACCESS_TOKEN, value) }

override var refreshToken: String
get() = dataStore.getString(REFRESH_TOKEN, "") ?: ""
set(value) = dataStore.edit { putString(REFRESH_TOKEN, value) }

override var userId: Long
get() = dataStore.getLong(USER_ID, 0L)
set(value) = dataStore.edit { putLong(USER_ID, value) }

override fun clearInfo() {
dataStore.edit().clear().commit()
}

companion object {
private const val ACCESS_TOKEN = "ACCESS_TOKEN"
private const val REFRESH_TOKEN = "REFRESH_TOKEN"
private const val USER_ID = "USER_ID"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.terning.data.repositoryimpl

import com.terning.data.datasource.AuthDataSource
import com.terning.data.dto.request.toSignInRequestDto
import com.terning.domain.entity.request.SignInRequestModel
import com.terning.domain.entity.response.SignInResponseModel
import com.terning.domain.repository.AuthRepository
import javax.inject.Inject

class AuthRepositoryImpl @Inject constructor(
private val authDataSource: AuthDataSource
) : AuthRepository {
override suspend fun postSignIn(
authorization: String,
request: SignInRequestModel
): Result<SignInResponseModel> = kotlin.runCatching {
authDataSource.postSignIn(
authorization,
request.toSignInRequestDto()
).result.toSignInModel()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.terning.data.repositoryimpl

import com.terning.data.datasource.CalendarDataSource
import com.terning.data.dto.request.CalendarDayListRequestDto
import com.terning.data.dto.request.CalendarMonthListRequestDto
import com.terning.data.dto.request.CalendarMonthRequestDto
import com.terning.domain.entity.response.CalendarScrapDetailModel
import com.terning.domain.entity.response.CalendarScrapModel
import com.terning.domain.repository.CalendarRepository
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import javax.inject.Inject

class CalendarRepositoryImpl @Inject constructor(
private val calendarDataSource: CalendarDataSource
) : CalendarRepository {
override suspend fun getScrapMonth(
year: Int,
month: Int
): Result<Map<String, List<CalendarScrapModel>>> =
runCatching {
val result = calendarDataSource.getCalendarMonth(
request = CalendarMonthRequestDto(
year = year,
month = month
)
).result

val scrapModelMapByDeadLine = result.flatMap { dto ->
dto.toScrapModelList()
}.groupBy { it.deadLine }

scrapModelMapByDeadLine
}

override suspend fun getScrapMonthList(
year: Int,
month: Int
): Result<Map<String, List<CalendarScrapDetailModel>>> =
runCatching {
val result = calendarDataSource.getCalendarMonthList(
request = CalendarMonthListRequestDto(
year = year,
month = month
)
).result

val scrapModelMapByDeadLine = result.flatMap { dto ->
dto.toScrapDetailModelList()
}.groupBy { it.deadLine }

scrapModelMapByDeadLine
}

override suspend fun getScrapDayList(
currentDate: LocalDate
): Result<List<CalendarScrapDetailModel>> =
runCatching {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val request = CalendarDayListRequestDto(currentDate.format(formatter))
val response = calendarDataSource.getCalendarDayList(request)
val scrapModelList = response.result.map { scrap ->
scrap.toScrapDetailModelList()
}
scrapModelList
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.terning.data.repositoryimpl

import com.terning.data.datasource.SearchDataSource
import com.terning.domain.entity.response.InternshipAnnouncement
import com.terning.domain.repository.SearchRepository
import javax.inject.Inject

class SearchViewsRepositoryImpl @Inject constructor(
private val searchDataSource: SearchDataSource,
) : SearchRepository {
override suspend fun getSearchViewsList(): Result<List<InternshipAnnouncement>> =
runCatching {
searchDataSource.getSearchViews().result.toSearchViewsEntity()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.terning.data.repositoryimpl

import com.terning.data.local.TerningDataStore
import com.terning.domain.repository.TokenRepository
import javax.inject.Inject

class TokenRepositoryImpl @Inject constructor(
private val terningDataStore: TerningDataStore
) : TokenRepository {
override fun getAccessToken(): String = terningDataStore.accessToken

override fun getRefreshToken(): String = terningDataStore.refreshToken

override fun setTokens(accessToken: String, refreshToken: String) {
terningDataStore.accessToken = accessToken
terningDataStore.refreshToken = refreshToken
}

override fun setUserId(userId: Long) {
terningDataStore.userId = userId
}

override fun clearInfo() {
terningDataStore.clearInfo()
}

}
16 changes: 16 additions & 0 deletions data/src/main/java/com/terning/data/service/AuthService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.terning.data.service

import com.terning.data.dto.BaseResponse
import com.terning.data.dto.request.SignInRequestDto
import com.terning.data.dto.response.SignInResponseDto
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST

interface AuthService {
@POST("api/v1/auth/sign-in")
suspend fun postSignIn(
@Header("Authorization") authorization: String,
@Body body: SignInRequestDto,
): BaseResponse<SignInResponseDto>
}
28 changes: 28 additions & 0 deletions data/src/main/java/com/terning/data/service/CalendarService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.terning.data.service

import com.terning.data.dto.BaseResponse
import com.terning.data.dto.response.CalendarMonthListResponseDto
import com.terning.data.dto.response.CalendarMonthResponseDto
import retrofit2.http.GET
import retrofit2.http.Query

interface CalendarService{
@GET("/api/v1/calendar/monthly-default")
suspend fun getCalendarScrapMonth(
@Query("year") year: Int,
@Query("month") month: Int
): BaseResponse<List<CalendarMonthResponseDto>>

@GET("/api/v1/calendar/monthly-list")
suspend fun getCalendarScrapMonthList(
@Query("year") year: Int,
@Query("month") month: Int
): BaseResponse<List<CalendarMonthListResponseDto>>

@GET("/api/v1/calendar/daily")
suspend fun getCalendarScrapDayList(
@Query("year") year: Int,
@Query("month") month: Int,
@Query("day") day: Int
): BaseResponse<List<CalendarMonthListResponseDto>>
}
12 changes: 0 additions & 12 deletions data/src/main/java/com/terning/data/service/MockService.kt

This file was deleted.

10 changes: 10 additions & 0 deletions data/src/main/java/com/terning/data/service/SearchService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.terning.data.service

import com.terning.data.dto.BaseResponse
import com.terning.data.dto.response.SearchViewsResponseDto
import retrofit2.http.GET

interface SearchService {
@GET("api/v1/search/views")
suspend fun getSearchViewsList(): BaseResponse<SearchViewsResponseDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.terning.domain.entity.request

data class SignInRequestModel(
val authType: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.terning.domain.entity.response

data class CalendarScrapDetailModel(
val scrapId: Long,
val internshipAnnouncementId: Long,
val title: String,
val dDay: String,
val workingPeriod: String,
val color: String,
val companyImage: String,
val startYear: Int,
val startMonth: Int,
val deadLine: String,
val isScrapped: Boolean = true
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.terning.domain.entity.response

data class ScrapModel(
data class CalendarScrapModel(
val scrapId: Long,
val title: String,
val deadLine: String,
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.terning.domain.entity.response

data class InternshipAnnouncement(
val title: String,
val companyImage: String,
val announcementId: Long,
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.domain.entity.response

data class SignInResponseModel(
val accessToken : String? ,
val refreshToken : String?,
val userId : Long,
val authType: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.terning.domain.repository

import com.terning.domain.entity.request.SignInRequestModel
import com.terning.domain.entity.response.SignInResponseModel

interface AuthRepository {
suspend fun postSignIn(
authorization: String,
request: SignInRequestModel
): Result<SignInResponseModel>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.terning.domain.repository

import com.terning.domain.entity.response.CalendarScrapDetailModel
import com.terning.domain.entity.response.CalendarScrapModel
import java.time.LocalDate

interface CalendarRepository{
suspend fun getScrapMonth(year: Int, month: Int): Result<Map<String, List<CalendarScrapModel>>>
suspend fun getScrapMonthList(year: Int, month: Int): Result<Map<String,List<CalendarScrapDetailModel>>>
suspend fun getScrapDayList(currentDate: LocalDate): Result<List<CalendarScrapDetailModel>>
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.terning.domain.repository

import com.terning.domain.entity.response.InternshipAnnouncement

interface SearchRepository {
suspend fun getSearchViewsList(): Result<List<InternshipAnnouncement>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.terning.domain.repository

interface TokenRepository {
fun getAccessToken(): String

fun getRefreshToken(): String

fun setTokens(accessToken: String, refreshToken: String)

fun setUserId(userId: Long)

fun clearInfo()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.terning.feature.calendar.calendar

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
@@ -11,7 +9,6 @@ import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold
@@ -26,7 +23,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
@@ -37,16 +33,14 @@ import androidx.navigation.compose.rememberNavController
import com.terning.core.designsystem.component.topappbar.CalendarTopAppBar
import com.terning.core.designsystem.theme.Grey200
import com.terning.core.extension.toast
import com.terning.feature.R
import com.terning.feature.calendar.calendar.component.ScreenTransition
import com.terning.feature.calendar.calendar.component.WeekDaysHeader
import com.terning.feature.calendar.calendar.model.CalendarState
import com.terning.feature.calendar.month.CalendarMonthScreen
import com.terning.feature.calendar.scrap.CalendarScrapList
import com.terning.feature.calendar.week.CalendarWeekWithScrap
import com.terning.feature.calendar.scrap.CalendarListScreen
import com.terning.feature.calendar.week.CalendarWeekScreen
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import java.time.LocalDate
import java.time.YearMonth

@Composable
@@ -59,7 +53,7 @@ fun CalendarRoute(
}

@Composable
fun CalendarScreen(
private fun CalendarScreen(
modifier: Modifier = Modifier,
navController: NavController = rememberNavController(),
viewModel: CalendarViewModel = hiltViewModel()
@@ -168,24 +162,20 @@ fun CalendarScreen(
)
},
contentTwo = {
CalendarWeekWithScrap(
CalendarWeekScreen(
modifier = Modifier
.fillMaxSize(),
selectedDate = calendarUiState,
scrapLists = viewModel.mockScrapList,
onDateSelected = {
viewModel.updateSelectedDate(it)
}
viewModel = viewModel
)
}
)
}
},
contentTwo = {
CalendarScrapList(
scrapList = viewModel.mockScrapList,
CalendarListScreen(
listState = listState,
pages = state.getPageCount(),
viewModel = viewModel,
modifier = Modifier
.fillMaxSize()
.padding(top = paddingValues.calculateTopPadding())
Original file line number Diff line number Diff line change
@@ -2,18 +2,12 @@ package com.terning.feature.calendar.calendar

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.terning.core.designsystem.theme.CalBlue1
import com.terning.core.designsystem.theme.CalGreen1
import com.terning.core.designsystem.theme.CalGreen2
import com.terning.core.designsystem.theme.CalPink
import com.terning.core.designsystem.theme.CalPurple
import com.terning.core.designsystem.theme.CalRed
import com.terning.core.designsystem.theme.CalYellow
import com.terning.core.state.UiState
import com.terning.domain.repository.ScrapRepository
import com.terning.domain.repository.CalendarRepository
import com.terning.feature.R
import com.terning.feature.calendar.month.ScrapCalendarState
import com.terning.feature.calendar.scrap.model.Scrap
import com.terning.feature.calendar.month.CalendarMonthState
import com.terning.feature.calendar.scrap.CalendarListState
import com.terning.feature.calendar.week.CalendarWeekState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -28,10 +22,10 @@ import javax.inject.Inject

@HiltViewModel
class CalendarViewModel @Inject constructor(
private val scrapRepository: ScrapRepository
private val calendarRepository: CalendarRepository
) : ViewModel() {

private var _selectedDate:MutableStateFlow<CalendarUiState> = MutableStateFlow(
private var _selectedDate: MutableStateFlow<CalendarUiState> = MutableStateFlow(
CalendarUiState(
selectedDate = LocalDate.now(),
isListEnabled = false
@@ -40,10 +34,16 @@ class CalendarViewModel @Inject constructor(

val selectedDate get() = _selectedDate.asStateFlow()

private val _scrapCalendarState = MutableStateFlow(ScrapCalendarState())
val scrapCalendarState = _scrapCalendarState.asStateFlow()
private val _calendarMonthState = MutableStateFlow(CalendarMonthState())
val calendarMonthState = _calendarMonthState.asStateFlow()

private val _calendarSideEffect:MutableSharedFlow<CalendarSideEffect> = MutableSharedFlow()
private val _calendarListState = MutableStateFlow(CalendarListState())
val calendarListState = _calendarListState.asStateFlow()

private val _calendarWeekState = MutableStateFlow(CalendarWeekState())
val calendarWeekState = _calendarWeekState.asStateFlow()

private val _calendarSideEffect: MutableSharedFlow<CalendarSideEffect> = MutableSharedFlow()
val calendarSideEffect = _calendarSideEffect.asSharedFlow()

fun updateSelectedDate(date: LocalDate) = viewModelScope.launch {
@@ -54,6 +54,7 @@ class CalendarViewModel @Inject constructor(
isWeekEnabled = true
)
}
getScrapWeekList()
} else {
_selectedDate.update { currentState ->
currentState.copy(
@@ -83,138 +84,69 @@ class CalendarViewModel @Inject constructor(
year: Int, month: Int
) = viewModelScope.launch {
withContext(Dispatchers.IO) {
scrapRepository.getScrapMonth(year, month)
calendarRepository.getScrapMonth(year, month)
}.fold(
onSuccess = {
_scrapCalendarState.update { currentState ->
_calendarMonthState.update { currentState ->
currentState.copy(
loadState = UiState.Success(it)
)
}
_calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_success))
},
onFailure = {
_calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_failure))
}
)
}

fun getScrapMonthList(
year: Int, month: Int
) = viewModelScope.launch {
withContext(Dispatchers.IO) {
calendarRepository.getScrapMonthList(year, month)
}.fold(
onSuccess = {
_calendarListState.update { currentState ->
currentState.copy(
loadState = if (it.isNotEmpty()) UiState.Success(it) else UiState.Empty
//loadState = UiState.Success(it)
)
}
},
onFailure = {
_calendarListState.update { currentState ->
currentState.copy(
loadState = UiState.Failure(it.message.toString())
)

//To be erased in future
val mockScrapList: List<List<Scrap>>
get() {
val list: MutableList<List<Scrap>> = mutableListOf()
for (i in 0..30) {
when (i % 6) {
0 -> {
list.add(
i,
listOf()
)
}

1 -> {
list.add(
i,
listOf(
Scrap(
"Task1_1",
CalBlue1,
dDay = "1",
period = "3",
isScraped = true
),
)
)
}

2 -> {
list.add(
i,
listOf(
Scrap(
"Task2_1",
CalPink,
dDay = "2",
period = "3",
isScraped = true
),
Scrap(
"Task2_2",
CalGreen1,
dDay = "2",
period = "3",
isScraped = true
)
)
)
}

3 -> {
list.add(
i,
listOf()
)
}

4 -> {
list.add(
i,
listOf()
)
}

5 -> {
list.add(
i,
listOf(
Scrap(
"Task3_1",
CalPink,
dDay = "5",
period = "3",
isScraped = true
),
Scrap(
"Task3_2",
CalPurple,
dDay = "5",
period = "3",
isScraped = true
),
Scrap(
"Task3_3",
CalRed,
dDay = "5",
period = "3",
isScraped = true
),
Scrap(
"Task3_4",
CalBlue1,
dDay = "5",
period = "3",
isScraped = true
),
Scrap(
"Task3_5",
CalGreen2,
dDay = "5",
period = "3",
isScraped = true
),
Scrap(
"Task3_6",
CalYellow,
dDay = "5",
period = "3",
isScraped = true
)
)
)
}
}
_calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_failure))
}
return list.toList()
}
)
}

fun getScrapWeekList() = viewModelScope.launch {
withContext(Dispatchers.IO) {
calendarRepository.getScrapDayList(_selectedDate.value.selectedDate)
}.fold(
onSuccess = {
_calendarWeekState.update { currentState ->
currentState.copy(
loadState = if (it.isNotEmpty()) UiState.Success(it) else UiState.Empty
//loadState = UiState.Success(it)
)
}
_calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_success))
},
onFailure = {
_calendarWeekState.update { currentState ->
currentState.copy(
loadState = UiState.Failure(it.message.toString())
)

}
_calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_failure))
}
)
}
}
Original file line number Diff line number Diff line change
@@ -12,8 +12,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.terning.core.designsystem.theme.Grey150
import com.terning.core.designsystem.theme.TerningPointTheme
import com.terning.core.extension.getDateAsMapString
import com.terning.core.extension.isToday
import com.terning.domain.entity.response.ScrapModel
import com.terning.domain.entity.response.CalendarScrapModel
import com.terning.feature.calendar.calendar.CalendarUiState
import com.terning.feature.calendar.month.component.CalendarDay
import com.terning.feature.calendar.month.model.MonthData
@@ -27,7 +28,7 @@ fun CalendarMonth(
monthData: MonthData,
onDateSelected: (LocalDate) -> Unit,
calendarUiState: CalendarUiState,
scrapMap: Map<String, List<ScrapModel>> = mapOf()
scrapMap: Map<String, List<CalendarScrapModel>> = mapOf()
) {
Column(
modifier = modifier
@@ -53,7 +54,7 @@ fun CalendarMonth(
onDateSelected = onDateSelected
)
if(!day.isOutDate) {
val index = day.date.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"))
val index = day.date.getDateAsMapString()
CalendarScrapStrip(
scrapLists = scrapMap[index].orEmpty()
)
Original file line number Diff line number Diff line change
@@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import com.terning.core.designsystem.theme.White
import com.terning.core.state.UiState
import com.terning.domain.entity.response.ScrapModel
import com.terning.domain.entity.response.CalendarScrapModel
import com.terning.feature.calendar.calendar.CalendarUiState
import com.terning.feature.calendar.calendar.CalendarViewModel
import com.terning.feature.calendar.calendar.model.CalendarDefaults.flingBehavior
import com.terning.feature.calendar.calendar.model.CalendarState.Companion.getDateByPage
import com.terning.feature.calendar.calendar.CalendarViewModel
import com.terning.feature.calendar.month.model.MonthData
import com.terning.feature.calendar.calendar.CalendarUiState
import kotlinx.coroutines.flow.distinctUntilChanged
import java.time.LocalDate
import java.time.YearMonth
@@ -31,7 +31,7 @@ fun CalendarMonthScreen(
modifier: Modifier = Modifier,
viewModel: CalendarViewModel = hiltViewModel()
) {
val scrapState by viewModel.scrapCalendarState.collectAsState()
val scrapState by viewModel.calendarMonthState.collectAsState()

LaunchedEffect(key1 = listState) {
snapshotFlow { listState.firstVisibleItemIndex }
@@ -48,25 +48,25 @@ fun CalendarMonthScreen(
UiState.Empty -> {}
is UiState.Failure -> {}
is UiState.Success -> {
val scrapMap = (scrapState.loadState as UiState.Success).data
HorizontalCalendar(
pages = pages,
modifier = modifier,
listState = listState,
calendarUiState = calendarUiState,
scrapMap = scrapMap,
onDateSelected = onDateSelected,
listState = listState,
scrapMap = (scrapState.loadState as UiState.Success).data
modifier = modifier
)
}
}
}


@Composable
fun HorizontalCalendar(
pages: Int,
listState: LazyListState,
calendarUiState: CalendarUiState,
scrapMap: Map<String, List<ScrapModel>>,
scrapMap: Map<String, List<CalendarScrapModel>>,
onDateSelected: (LocalDate) -> Unit,
modifier: Modifier = Modifier,
) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.feature.calendar.month

import com.terning.core.state.UiState
import com.terning.domain.entity.response.CalendarScrapModel

data class CalendarMonthState (
val loadState: UiState<Map<String, List<CalendarScrapModel>>> = UiState.Loading
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.terning.feature.calendar.scrap

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.terning.core.designsystem.theme.Back
import com.terning.core.designsystem.theme.Grey400
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.core.extension.getDateAsMapString
import com.terning.core.extension.isListNotEmpty
import com.terning.core.state.UiState
import com.terning.feature.R
import com.terning.feature.calendar.calendar.CalendarViewModel
import com.terning.feature.calendar.calendar.model.CalendarDefaults.flingBehavior
import com.terning.feature.calendar.calendar.model.CalendarState.Companion.getDateByPage
import com.terning.feature.calendar.scrap.component.CalendarScrapList
import kotlinx.coroutines.flow.distinctUntilChanged
import java.time.LocalDate

@Composable
fun CalendarListScreen(
pages: Int,
listState: LazyListState,
modifier: Modifier = Modifier,
viewModel: CalendarViewModel = hiltViewModel()
) {
val lifecycleOwner = LocalLifecycleOwner.current
val scrapState by viewModel.calendarListState.collectAsStateWithLifecycle(lifecycleOwner)

LaunchedEffect(key1 = listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.distinctUntilChanged()
.collect {
val page = listState.firstVisibleItemIndex
val date = getDateByPage(page)
viewModel.getScrapMonthList(date.year, date.monthValue)
}
}


LazyRow(
modifier = modifier
.background(White),
state = listState,
userScrollEnabled = true,
flingBehavior = flingBehavior(
state = listState
)
) {
items(pages) { page ->
val getDate = getDateByPage(page)

LazyColumn(
modifier = Modifier
.fillParentMaxWidth()
.fillMaxHeight()
.background(Back)
) {
when (scrapState.loadState) {
UiState.Loading -> {}
UiState.Empty -> {
item {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
modifier = Modifier
.padding(top = 42.dp)
.fillMaxWidth(),
text = stringResource(id = R.string.calendar_empty_scrap),
textAlign = TextAlign.Center,
style = TerningTheme.typography.body5,
color = Grey400
)
}
}
}

is UiState.Failure -> {}
is UiState.Success -> {
items(getDate.lengthOfMonth()) { day ->
val scrapMap = (scrapState.loadState as UiState.Success).data
val currentDate =
LocalDate.of(getDate.year, getDate.monthValue, day + 1)
val dateIndex = currentDate.getDateAsMapString()

if (scrapMap[dateIndex].isListNotEmpty()) {
CalendarScrapList(
selectedDate = currentDate,
scrapList = scrapMap[dateIndex].orEmpty(),
isFromList = true,

noScrapScreen = {})
}
}
}
}
}
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.feature.calendar.scrap

import com.terning.core.state.UiState
import com.terning.domain.entity.response.CalendarScrapDetailModel

data class CalendarListState (
val loadState: UiState<Map<String, List<CalendarScrapDetailModel>>> = UiState.Loading
)
Original file line number Diff line number Diff line change
@@ -15,12 +15,12 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.domain.entity.response.ScrapModel
import com.terning.domain.entity.response.CalendarScrapModel

@Composable
fun CalendarScrapStrip(
modifier: Modifier = Modifier,
scrapLists: List<ScrapModel>
scrapLists: List<CalendarScrapModel>
) {
LazyColumn(
modifier = modifier.fillMaxWidth()

This file was deleted.

Original file line number Diff line number Diff line change
@@ -2,28 +2,29 @@ package com.terning.feature.calendar.scrap.component

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.terning.core.designsystem.component.item.InternItem
import com.terning.core.designsystem.component.item.ScrapBox
import com.terning.feature.calendar.scrap.model.Scrap
import com.terning.domain.entity.response.CalendarScrapDetailModel

@Composable
fun CalendarScrap(
scrap: Scrap,
scrap: CalendarScrapDetailModel,
modifier: Modifier = Modifier,
) {
ScrapBox(
modifier = modifier,
cornerRadius = 10.dp,
scrapColor = scrap.backgroundColor,
scrapColor = Color(android.graphics.Color.parseColor(scrap.color)),
elevation = 1.dp,
) {
InternItem(
imageUrl = scrap.image.orEmpty(),
title = scrap.text,
imageUrl = scrap.companyImage.orEmpty(),
title = scrap.title,
dateDeadline = scrap.dDay,
workingPeriod = scrap.period,
isScraped = scrap.isScraped
workingPeriod = scrap.workingPeriod,
isScraped = scrap.isScrapped
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.terning.feature.calendar.scrap.component

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.terning.core.designsystem.theme.Black
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.extension.getDateStringInKorean
import com.terning.core.extension.isListNotEmpty
import com.terning.domain.entity.response.CalendarScrapDetailModel
import java.time.LocalDate

@Composable
fun CalendarScrapList(
selectedDate: LocalDate,
scrapList: List<CalendarScrapDetailModel>,
isFromList: Boolean = false,
noScrapScreen: @Composable () -> Unit
) {
val scrollState = rememberScrollState()

if (scrapList.isListNotEmpty()) {
Text(
text = selectedDate.getDateStringInKorean(),
style = TerningTheme.typography.title5,
color = Black,
modifier = Modifier.padding(start = 24.dp, top = 16.dp, bottom = 15.dp)
)
}
val topModifier = if (!isFromList) {
Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
.verticalScroll(scrollState)
} else {
Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
}

if (!scrapList.isListNotEmpty()) {
noScrapScreen()
} else {
Column(
modifier = topModifier
) {
for (scrap in scrapList) {
CalendarScrap(
scrap = scrap
)
Spacer(
modifier = Modifier.height(12.dp)
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.terning.feature.calendar.week

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.Text
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.shadow
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.terning.core.designsystem.theme.Back
import com.terning.core.designsystem.theme.Grey200
import com.terning.core.designsystem.theme.Grey400
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.core.state.UiState
import com.terning.domain.entity.response.CalendarScrapDetailModel
import com.terning.feature.R
import com.terning.feature.calendar.calendar.CalendarViewModel
import com.terning.feature.calendar.scrap.component.CalendarScrapList
import java.time.LocalDate

@Composable
fun CalendarWeekScreen(
modifier: Modifier = Modifier,
viewModel: CalendarViewModel = hiltViewModel()
) {
val lifecycleOwner = LocalLifecycleOwner.current
val uiState by viewModel.selectedDate.collectAsStateWithLifecycle(lifecycleOwner)
val calendarWeekState by viewModel.calendarWeekState.collectAsStateWithLifecycle(lifecycleOwner)

LaunchedEffect(uiState.selectedDate) {
viewModel.getScrapWeekList()
}

Column(
modifier = modifier
.background(Back)
) {
Card(
modifier = Modifier
.border(
width = 0.dp,
color = Grey200,
shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp)
)
.shadow(
shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp),
elevation = 1.dp
),

shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp),
) {
HorizontalCalendarWeek(
modifier = Modifier
.fillMaxWidth()
.background(White),
selectedDate = uiState,
onDateSelected = {
viewModel.updateSelectedDate(it)
}
)
}

when (calendarWeekState.loadState) {
is UiState.Loading -> {}
is UiState.Empty -> {
CalendarWeekEmpty()
}
is UiState.Failure -> {}
is UiState.Success -> {
val scrapList = (calendarWeekState.loadState as UiState.Success).data
CalendarWeekSuccess(scrapList = scrapList, selectedDate = uiState.selectedDate)
}
}
}
}

@Composable
fun CalendarWeekEmpty(
modifier: Modifier = Modifier
) {
Box(
contentAlignment = Alignment.Center
) {


Text(
modifier = Modifier
.padding(top = 42.dp)
.fillMaxWidth(),
text = stringResource(id = R.string.calendar_empty_scrap),
textAlign = TextAlign.Center,
style = TerningTheme.typography.body5,
color = Grey400
)
}
}

@Composable
fun CalendarWeekSuccess(
scrapList: List<CalendarScrapDetailModel>,
selectedDate: LocalDate,
) {

CalendarScrapList(selectedDate = selectedDate, scrapList = scrapList) {}
}




Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.feature.calendar.week

import com.terning.core.state.UiState
import com.terning.domain.entity.response.CalendarScrapDetailModel

data class CalendarWeekState (
val loadState: UiState<List<CalendarScrapDetailModel>> = UiState.Loading
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ import java.time.LocalDate
import java.time.YearMonth

@Composable
fun CalendarWeek(
fun HorizontalCalendarWeek(
selectedDate: CalendarUiState,
modifier: Modifier = Modifier,
onDateSelected: (LocalDate) -> Unit = {}
Original file line number Diff line number Diff line change
@@ -71,7 +71,9 @@ fun InternScreen(
color = Grey200,
offsetY = 2.dp
),
onBackButtonClick = {},
onBackButtonClick = {
navController.navigateUp()
},
)
},
bottomBar = {
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.terning.feature.mypage

import androidx.lifecycle.ViewModel
import com.terning.domain.repository.MockRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class MyPageViewModel @Inject constructor(private val repository: MockRepository) : ViewModel() {
class MyPageViewModel @Inject constructor() : ViewModel() {


}
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ fun SignInRoute(
when (sideEffect) {
is SignInSideEffect.ShowToast -> context.toast(sideEffect.message)
is SignInSideEffect.NavigateToHome -> navController.navigateSignUp()
is SignInSideEffect.NavigateSignUp -> navController.navigateSignUp()
}
}
}
Original file line number Diff line number Diff line change
@@ -4,5 +4,6 @@ import androidx.annotation.StringRes

sealed class SignInSideEffect {
data object NavigateToHome : SignInSideEffect()
data object NavigateSignUp : SignInSideEffect()
data class ShowToast(@StringRes val message: Int) : SignInSideEffect()
}
Original file line number Diff line number Diff line change
@@ -8,6 +8,9 @@ import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import com.terning.core.state.UiState
import com.terning.domain.entity.request.SignInRequestModel
import com.terning.domain.repository.AuthRepository
import com.terning.domain.repository.TokenRepository
import com.terning.feature.R
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -20,7 +23,14 @@ import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SignInViewModel @Inject constructor() : ViewModel() {
class SignInViewModel @Inject constructor(
private val authRepository: AuthRepository,
private val tokenRepository: TokenRepository
) : ViewModel() {

init {
tokenRepository.clearInfo()
}

private val _signInState = MutableStateFlow(SignInState())
val signInState: StateFlow<SignInState>
@@ -47,7 +57,7 @@ class SignInViewModel @Inject constructor() : ViewModel() {
if (error != null) {
signInFailure(context, error)
} else if (token != null) {
signInSuccess(token)
signInSuccess(token.accessToken)
}
}
}
@@ -72,15 +82,36 @@ class SignInViewModel @Inject constructor() : ViewModel() {
}
}

private fun signInSuccess(token: OAuthToken) {
viewModelScope.launch {
_signInState.value =
_signInState.value.copy(accessToken = UiState.Success(token.accessToken))
_signInSideEffects.emit(SignInSideEffect.NavigateToHome)
private suspend fun signInSuccess(
accessToken: String,
platform: String = KAKAO
) {
authRepository.postSignIn(
accessToken,
SignInRequestModel(platform)
).onSuccess { response ->
when {
response.accessToken == null -> _signInSideEffects.emit(SignInSideEffect.NavigateSignUp)

else -> {
tokenRepository.setTokens(
response.accessToken ?: return,
response.refreshToken ?: return
)
tokenRepository.setUserId(response.userId)

_signInSideEffects.emit(SignInSideEffect.NavigateToHome)
}
}
}.onFailure {
_signInState.value = SignInState(UiState.Failure(it.message.toString()))
_signInSideEffects.emit(SignInSideEffect.ShowToast(R.string.server_failure))
}

}

companion object {
private const val KAKAO_NOT_LOGGED_IN = "statusCode=302"
private const val KAKAO = "KAKAO"
}
}
Original file line number Diff line number Diff line change
@@ -10,16 +10,24 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.navigation.NavHostController
import com.terning.core.designsystem.component.textfield.SearchTextField
import com.terning.core.designsystem.component.topappbar.LogoTopAppBar
import com.terning.core.designsystem.theme.Black
import com.terning.core.designsystem.theme.Grey100
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.extension.noRippleClickable
import com.terning.core.state.UiState
import com.terning.domain.entity.response.InternshipAnnouncement
import com.terning.feature.R
import com.terning.feature.search.search.component.ImageSlider
import com.terning.feature.search.search.component.InternListType
@@ -29,16 +37,46 @@ import com.terning.feature.search.searchprocess.navigation.navigateSearchProcess
@Composable
fun SearchRoute(
navController: NavHostController,
viewModel: SearchViewModel = hiltViewModel(),
) {
SearchScreen(
navController = navController
)
val lifecycleOwner = LocalLifecycleOwner.current

val state by viewModel.state.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)

LaunchedEffect(key1 = true) {
viewModel.getSearchViews()
}

LaunchedEffect(viewModel.sideEffect, lifecycleOwner) {
viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle)
.collect { sideEffect ->
when (sideEffect) {
is SearchViewsSideEffect.Toast -> {
sideEffect.message
}
}
}
}

when (state.searchViewsList) {
is UiState.Loading -> {}
is UiState.Empty -> {}
is UiState.Failure -> {}
is UiState.Success -> {
SearchScreen(
navController = navController,
searchViewsList = (state.searchViewsList as UiState.Success<List<InternshipAnnouncement>>).data
)
}

}
}

@Composable
fun SearchScreen(
modifier: Modifier = Modifier,
navController: NavHostController,
searchViewsList: List<InternshipAnnouncement>,
) {
val images = listOf(
R.drawable.ic_nav_search,
@@ -91,13 +129,21 @@ fun SearchScreen(
color = Black
)

SearchInternList(type = InternListType.VIEW)
SearchInternList(
type = InternListType.VIEW,
searchViewsList = searchViewsList,
navController = navController
)
HorizontalDivider(
thickness = 4.dp,
modifier = Modifier.padding(vertical = 8.dp),
color = Grey100,
)
SearchInternList(type = InternListType.SCRAP)
SearchInternList(
type = InternListType.SCRAP,
searchViewsList = searchViewsList,
navController = navController
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.terning.feature.search.search

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.terning.core.state.UiState
import com.terning.domain.repository.SearchRepository
import com.terning.feature.R
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SearchViewModel @Inject constructor(
private val searchRepository: SearchRepository,
) : ViewModel() {
private val _state: MutableStateFlow<SearchViewsState> = MutableStateFlow(SearchViewsState())
val state: StateFlow<SearchViewsState> = _state.asStateFlow()

private val _sideEffect: MutableSharedFlow<SearchViewsSideEffect> = MutableSharedFlow()
val sideEffect = _sideEffect.asSharedFlow()

init {
getSearchViews()
}

fun getSearchViews() {
viewModelScope.launch {
searchRepository.getSearchViewsList().onSuccess { searchViewsList ->
_state.value = _state.value.copy(
searchViewsList = UiState.Success(searchViewsList)
)
}.onFailure {
_sideEffect.emit(SearchViewsSideEffect.Toast(R.string.server_failure))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.terning.feature.search.search

import androidx.annotation.StringRes

sealed class SearchViewsSideEffect {
data class Toast(@StringRes val message: Int) : SearchViewsSideEffect()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.feature.search.search

import com.terning.core.state.UiState
import com.terning.domain.entity.response.InternshipAnnouncement

data class SearchViewsState(
var searchViewsList: UiState<List<InternshipAnnouncement>> = UiState.Loading,
)
Original file line number Diff line number Diff line change
@@ -1,59 +1,81 @@
package com.terning.feature.search.search.component

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.terning.core.designsystem.theme.Black
import com.terning.core.designsystem.theme.Grey400
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.core.extension.customShadow
import com.terning.core.extension.noRippleClickable
import com.terning.domain.entity.response.InternshipAnnouncement
import com.terning.feature.R
import com.terning.feature.intern.navigation.navigateIntern

@Composable
fun SearchIntern() {
fun SearchIntern(
searchViews: InternshipAnnouncement,
navController: NavHostController,
) {
Column(
verticalArrangement = Arrangement.spacedBy(
10.dp,
Alignment.Top
),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.shadow(
elevation = 4.dp,
// TODO ํšจ๋นˆ ๊ทธ๋ฆผ์ž PR ํ’€ ๋ฐ›์•„์„œ ๋ฐ”๊พธ๊ธฐ
spotColor = Color(0x26DDDDDD),
ambientColor = Color(0x26DDDDDD)
)
.width(140.dp)
.background(
color = White,
shape = RoundedCornerShape(size = 5.dp)
)
.padding(vertical = 8.dp)
.noRippleClickable {
navController.navigateIntern()
}
) {
Image(
painter = painterResource(id = R.drawable.ic_nav_search),
contentDescription = null,
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(searchViews.companyImage)
.crossfade(true)
.build(),
contentDescription = stringResource(id = R.string.search_image),
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.background(Grey400)
.height(76.dp)
.customShadow(offsetY = 3.dp)
.wrapContentSize()
.clip(
RoundedCornerShape(
topStart = 5.dp,
topEnd = 5.dp
)
)
)

Text(
text = "[์œ ํ•œํ‚ด๋ฒŒ๋ฆฌ]\n๊ทธ๋ฆฐ์บ ํ”„ w. ๋Œ€ํ•™์ƒ ์ˆฒ \nํ™œ๋™๊ฐ€ ๋ชจ์ง‘",
modifier = Modifier.padding(horizontal = 8.dp),
text = "${searchViews.title} ${searchViews.title}${searchViews.title}${searchViews.title}",
modifier = Modifier
.padding(horizontal = 8.dp)
.wrapContentSize(),
style = TerningTheme.typography.body6,
color = Black,
overflow = TextOverflow.Ellipsis,
Original file line number Diff line number Diff line change
@@ -9,13 +9,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.terning.core.designsystem.theme.Grey400
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.domain.entity.response.InternshipAnnouncement
import com.terning.feature.R

@Composable
fun SearchInternList(
type: InternListType,
searchViewsList: List<InternshipAnnouncement>,
navController: NavHostController
) {
Column(modifier = Modifier.padding(horizontal = 24.dp)) {
Text(
@@ -33,8 +37,11 @@ fun SearchInternList(
modifier = Modifier.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
items(5) {
SearchIntern()
items(searchViewsList.size) { index ->
SearchIntern(
searchViews = searchViewsList[index],
navController = navController
)
}
}
}
5 changes: 3 additions & 2 deletions feature/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@
<resources>
<string name="app_name">Terning-Android</string>

<string name="server_success">์„œ๋ฒ„ํ†ต์‹  ์„ฑ๊ณต</string>
<string name="server_failure">์„œ๋ฒ„ํ†ต์‹  ์‹คํŒจ</string>
<string name="server_success">์„œ๋ฒ„ํ†ต์‹ ์— ์„ฑ๊ณตํ–ˆ์–ด์š”</string>
<string name="server_failure">์„œ๋ฒ„ํ†ต์‹ ์— ์‹คํŒจํ–ˆ์–ด์š”</string>

<!--BottomNavigation-->
<string name="bottom_nav_home">ํ™ˆ</string>
@@ -31,6 +31,7 @@
<string name="search_text_field_hint">๊ด€์‹ฌ์žˆ๋Š” ์ธํ„ด ๊ณต๊ณ  ํ‚ค์›Œ๋“œ๋ฅผ ๊ฒ€์ƒ‰ํ•ด ๋ณด์„ธ์š”</string>
<string name="search_today_popular">์š”์ฆ˜ ๋Œ€ํ•™์ƒ๋“ค์—๊ฒŒ ์ธ๊ธฐ ์žˆ๋Š” ๊ณต๊ณ </string>
<string name="search_most_view_intern">์ง€๊ธˆ ์กฐํšŒ์ˆ˜๊ฐ€ ๋งŽ์€ ๊ณต๊ณ ๋“ค์ด์—์š”</string>
<string name="search_image">๊ณต๊ณ  ์ด๋ฏธ์ง€</string>
<string name="search_most_scrap_intern">์ง€๊ธˆ ์Šคํฌ๋žฉ์ˆ˜๊ฐ€ ๋งŽ์€ ๊ณต๊ณ ๋“ค์ด์—์š”</string>

<!--SearchProcess-->

0 comments on commit 3f9a40d

Please sign in to comment.