Skip to content

Commit

Permalink
Merge pull request #872 from sopt-makers/feature/869
Browse files Browse the repository at this point in the history
[Feature/#869] 오늘의 솝마디 대시보드 기능 구현
  • Loading branch information
s9hn committed Sep 30, 2024
2 parents 6a1698b + 9384ee5 commit 23fbdb9
Show file tree
Hide file tree
Showing 31 changed files with 608 additions and 44 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ dependencies {
implementation(projects.domain.soptamp)
implementation(projects.domain.mypage)
implementation(projects.domain.poke)
implementation(projects.domain.fortune)
implementation(projects.domain.notification)
implementation(projects.feature.soptamp)
implementation(projects.data.fortune)
implementation(projects.data.soptamp)
implementation(projects.data.mypage)
implementation(projects.data.poke)
Expand Down
1 change: 1 addition & 0 deletions data/fortune/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
40 changes: 40 additions & 0 deletions data/fortune/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* MIT License
* Copyright 2023-2024 SOPT - Shout Our Passion Together
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

plugins {
sopt("feature")
}

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

dependencies {
implementation(projects.domain.fortune)
implementation(projects.core.network)
implementation(projects.core.common)
implementation(platform(libs.okhttp.bom))
implementation(libs.bundles.okhttp)
}
Empty file added data/fortune/consumer-rules.pro
Empty file.
27 changes: 27 additions & 0 deletions data/fortune/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
MIT License
Copyright (c) 2023-2024 SOPT Makers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<manifest>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sopt.official.data.fortune.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.fortune.remote.api.FortuneApi
import retrofit2.Retrofit
import retrofit2.create
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal object ApiModule {

@Provides
@Singleton
internal fun provideFortuneApi(@AppRetrofit(true) retrofit: Retrofit): FortuneApi = retrofit.create()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.sopt.official.data.fortune.di

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.official.data.fortune.repository.DefaultFortuneRepository
import org.sopt.official.domain.fortune.repository.FortuneRepository
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal interface RepositoryModule {

@Binds
@Singleton
abstract fun bindDefaultFortuneRepository(defaultFortuneRepository: DefaultFortuneRepository): FortuneRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.sopt.official.data.fortune.mapper

import org.sopt.official.data.fortune.remote.response.TodayFortuneCardResponse
import org.sopt.official.data.fortune.remote.response.TodayFortuneWordResponse
import org.sopt.official.domain.fortune.model.TodayFortuneCard
import org.sopt.official.domain.fortune.model.TodayFortuneWord

internal fun TodayFortuneCardResponse.toDomain(): TodayFortuneCard = TodayFortuneCard(
description = description,
imageColorCode = imageColorCode,
imageUrl = imageUrl,
name = name,
)

internal fun TodayFortuneWordResponse.toDomain(): TodayFortuneWord = TodayFortuneWord(
userName = userName,
title = title,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.official.data.fortune.remote.api

import org.sopt.official.data.fortune.remote.response.TodayFortuneCardResponse
import org.sopt.official.data.fortune.remote.response.TodayFortuneWordResponse
import retrofit2.http.GET
import retrofit2.http.Query

internal interface FortuneApi {

@GET("fortune/word")
suspend fun getTodayFortuneWord(
@Query("todayDate") todayDate: String,
): TodayFortuneWordResponse

@GET("fortune/card/today")
suspend fun getTodayFortuneCard(): TodayFortuneCardResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.sopt.official.data.fortune.remote.response

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

@Serializable
internal data class TodayFortuneCardResponse(
@SerialName("description")
val description: String,
@SerialName("imageColorCode")
val imageColorCode: String,
@SerialName("imageUrl")
val imageUrl: String,
@SerialName("name")
val name: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.sopt.official.data.fortune.remote.response

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

@Serializable
internal data class TodayFortuneWordResponse(
@SerialName("userName")
val userName: String,
@SerialName("title")
val title: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.official.data.fortune.repository

import org.sopt.official.data.fortune.mapper.toDomain
import org.sopt.official.data.fortune.remote.api.FortuneApi
import org.sopt.official.domain.fortune.model.TodayFortuneCard
import org.sopt.official.domain.fortune.model.TodayFortuneWord
import org.sopt.official.domain.fortune.repository.FortuneRepository
import javax.inject.Inject

internal class DefaultFortuneRepository @Inject constructor(
private val fortuneApi: FortuneApi,
) : FortuneRepository {

override suspend fun fetchTodayFortuneWord(date: String): TodayFortuneWord = fortuneApi.getTodayFortuneWord(date).toDomain()

override suspend fun fetchTodayFortuneCard(): TodayFortuneCard = fortuneApi.getTodayFortuneCard().toDomain()
}
1 change: 1 addition & 0 deletions domain/fortune/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
36 changes: 36 additions & 0 deletions domain/fortune/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* MIT License
* Copyright 2023-2024 SOPT - Shout Our Passion Together
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

plugins {
sopt("kotlin")
}

kotlin {
jvmToolchain(17)
}

dependencies {
implementation(libs.javax.inject)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.sopt.official.domain.fortune.model

data class TodayFortuneCard(
val description: String,
val imageColorCode: String,
val imageUrl: String,
val name: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.sopt.official.domain.fortune.model

data class TodayFortuneWord(
val userName: String,
val title: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.official.domain.fortune.repository

import org.sopt.official.domain.fortune.model.TodayFortuneCard
import org.sopt.official.domain.fortune.model.TodayFortuneWord

interface FortuneRepository {
suspend fun fetchTodayFortuneWord(date: String): TodayFortuneWord
suspend fun fetchTodayFortuneCard(): TodayFortuneCard
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.official.domain.fortune.usecase

import java.text.SimpleDateFormat
import java.util.Locale
import javax.inject.Inject

class GetTodayDateUseCase @Inject constructor() {

operator fun invoke(): String {
val currentDate = System.currentTimeMillis()

return SimpleDateFormat("yyyy-MM-dd", Locale.KOREAN).format(currentDate)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.official.domain.fortune.usecase

import org.sopt.official.domain.fortune.model.TodayFortuneWord
import org.sopt.official.domain.fortune.repository.FortuneRepository
import javax.inject.Inject

class GetTodayFortuneUseCase @Inject constructor(
private val fortuneRepository: FortuneRepository,
private val getTodayDateUseCase: GetTodayDateUseCase,
) {

suspend operator fun invoke(): TodayFortuneWord = fortuneRepository.fetchTodayFortuneWord(getTodayDateUseCase())
}
4 changes: 4 additions & 0 deletions feature/fortune/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
plugins {
sopt("feature")
sopt("compose")
sopt("test")
}

android {
namespace = "org.sopt.official.feature.fortune"
}

dependencies {
// domain
implementation(projects.domain.fortune)

// core
implementation(projects.core.common)
implementation(projects.core.designsystem)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.sopt.official.feature.fortune.fortuneDetail

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import org.junit.Rule
import org.junit.Test
import org.sopt.official.designsystem.SoptTheme
import org.sopt.official.feature.fortune.feature.fortuneDetail.FortuneDetailScreen
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.FortuneDetailUiState

internal class FortuneDetailScreenTest {

@get:Rule
val composeRule = createComposeRule()

@Test
fun 서버통신이_성공하면_이름_솝마디_날짜가_노출된다() {
// given:
val date = "2024-09-26"
val name = "이현우"
val content = "안녕하세요안녕하세요안녕하세요안녕하세요안녕하세요"

// when:

composeRule.setContent {
SoptTheme {
FortuneDetailScreen(
paddingValue = PaddingValues(),
date = date,
onFortuneAmuletClick = { },
uiState = FortuneDetailUiState.TodaySentence(
userName = name,
content = content,
)
)
}
}

// then:
val todayFortune = composeRule.onNodeWithContentDescription("todaySentence")
.fetchSemanticsNode().config.getOrNull(SemanticsProperties.Text)?.joinToString(separator = "").orEmpty()

composeRule.waitForIdle()

composeRule.onNodeWithText(date).assertIsDisplayed()
assert(todayFortune.contains(name))
assert(todayFortune.contains(content))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import org.sopt.official.designsystem.SoptTheme
import org.sopt.official.feature.fortune.component.FortuneTopBar
import org.sopt.official.feature.fortune.feature.fortundDetail.navigation.FortuneDetail
import org.sopt.official.feature.fortune.feature.fortundDetail.navigation.fortuneDetailNavGraph
import org.sopt.official.feature.fortune.feature.fortuneDetail.navigation.FortuneDetail
import org.sopt.official.feature.fortune.feature.fortuneDetail.navigation.fortuneDetailNavGraph
import org.sopt.official.feature.fortune.feature.fortuneAmulet.navigation.FortuneAmulet
import org.sopt.official.feature.fortune.feature.fortuneAmulet.navigation.fortuneAmuletNavGraph
import org.sopt.official.feature.fortune.feature.home.navigation.Home
Expand Down
Loading

0 comments on commit 23fbdb9

Please sign in to comment.