diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9e49256c5..eed75ff06 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,12 +34,13 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- - name: Generate google-services.json
+ - name: Create google-services.json
run: |
- printf "%s" "${{ secrets.GOOGLE_SERVICES }}" | base64 --decode > ./app/google-services.json
-
+ mkdir -p ${{ github.workspace }}/app
+ echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > ${{ github.workspace }}/app/google-services.json
+
- name: Build with Gradle
- run: ./gradlew build
+ run: ./gradlew assembleDebug --parallel
- name: Run test
run: ./gradlew test
diff --git a/app/src/dev/AndroidManifest.xml b/app/src/dev/AndroidManifest.xml
index 3d0dd1c6b..a480689de 100644
--- a/app/src/dev/AndroidManifest.xml
+++ b/app/src/dev/AndroidManifest.xml
@@ -11,18 +11,22 @@
+ android:name=".android.app.DevApplication"
+ tools:replace="android:name"
+ android:allowBackup="true"
+ android:enableOnBackInvokedCallback="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.DMS"
+ android:usesCleartextTraffic="true"
+ tools:targetApi="tiramisu">
+ android:screenOrientation="portrait">
CompositionLocalProvider(LocalResultStore provides resultStore) {
NavDisplay(
modifier = Modifier
- .background(DmsTheme.colorScheme.surfaceTint)
.padding(paddingValues),
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
diff --git a/app/src/dev/res/drawable/ic_launcher_background.xml b/app/src/dev/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..e93e11ade
--- /dev/null
+++ b/app/src/dev/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/dev/res/drawable/ic_launcher_foreground.xml b/app/src/dev/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..bd5df3f76
--- /dev/null
+++ b/app/src/dev/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/dev/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/dev/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..7353dbd1f
--- /dev/null
+++ b/app/src/dev/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/dev/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/dev/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..7353dbd1f
--- /dev/null
+++ b/app/src/dev/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/dev/res/mipmap-hdpi/ic_launcher.webp b/app/src/dev/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..583d0ec80
Binary files /dev/null and b/app/src/dev/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/dev/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/dev/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..3c9fac50a
Binary files /dev/null and b/app/src/dev/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/dev/res/mipmap-mdpi/ic_launcher.webp b/app/src/dev/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..55b51f4f8
Binary files /dev/null and b/app/src/dev/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/dev/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/dev/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9d9e9145d
Binary files /dev/null and b/app/src/dev/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/dev/res/mipmap-xhdpi/ic_launcher.webp b/app/src/dev/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..395a97c04
Binary files /dev/null and b/app/src/dev/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/dev/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/dev/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..aa7ac79ab
Binary files /dev/null and b/app/src/dev/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/dev/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/dev/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..b2b77aeab
Binary files /dev/null and b/app/src/dev/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/dev/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/dev/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..e23d28e3b
Binary files /dev/null and b/app/src/dev/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..fafb3427d
Binary files /dev/null and b/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/dev/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/dev/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..65581e360
Binary files /dev/null and b/app/src/dev/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/dev/res/values/ic_launcher_background.xml b/app/src/dev/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..c5d5899fd
--- /dev/null
+++ b/app/src/dev/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/dev/res/values/strings.xml b/app/src/dev/res/values/strings.xml
new file mode 100644
index 000000000..5b0c78e41
--- /dev/null
+++ b/app/src/dev/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ DMS
+
\ No newline at end of file
diff --git a/core/design-system/src/dev/res/drawable-night/ic_equal.xml b/core/design-system/src/dev/res/drawable-night/ic_equal.xml
new file mode 100644
index 000000000..e566f5b20
--- /dev/null
+++ b/core/design-system/src/dev/res/drawable-night/ic_equal.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/core/design-system/src/dev/res/drawable-night/ic_minus.xml b/core/design-system/src/dev/res/drawable-night/ic_minus.xml
new file mode 100644
index 000000000..8fea3687d
--- /dev/null
+++ b/core/design-system/src/dev/res/drawable-night/ic_minus.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/core/design-system/src/dev/res/drawable-night/ic_plus.xml b/core/design-system/src/dev/res/drawable-night/ic_plus.xml
new file mode 100644
index 000000000..677d42886
--- /dev/null
+++ b/core/design-system/src/dev/res/drawable-night/ic_plus.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/core/design-system/src/dev/res/drawable-night/img_morning.png b/core/design-system/src/dev/res/drawable-night/img_morning.png
new file mode 100644
index 000000000..b0c3bd9a3
Binary files /dev/null and b/core/design-system/src/dev/res/drawable-night/img_morning.png differ
diff --git a/core/device/build.gradle.kts b/core/device/build.gradle.kts
index 137dd4116..254a632c6 100644
--- a/core/device/build.gradle.kts
+++ b/core/device/build.gradle.kts
@@ -35,6 +35,17 @@ android {
kotlinOptions {
jvmTarget = Versions.java.toString()
}
+
+ flavorDimensions += "environment"
+
+ productFlavors {
+ create("dev") {
+ dimension = "environment"
+ }
+ create("prod") {
+ dimension = "environment"
+ }
+ }
}
dependencies {
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/di/DataSourceModule.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/di/DataSourceModule.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/di/DataSourceModule.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/di/DataSourceModule.kt
diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/di/StoreModule.kt b/core/device/src/dev/kotlin/team/aliens/dms/android/core/device/di/StoreModule.kt
similarity index 100%
rename from core/device/src/main/java/team/aliens/dms/android/core/device/di/StoreModule.kt
rename to core/device/src/dev/kotlin/team/aliens/dms/android/core/device/di/StoreModule.kt
diff --git a/core/device/src/main/AndroidManifest.xml b/core/device/src/main/AndroidManifest.xml
deleted file mode 100644
index a5918e68a..000000000
--- a/core/device/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt
new file mode 100644
index 000000000..71ba259fc
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt
@@ -0,0 +1,10 @@
+package team.aliens.dms.android.core.device.datastore
+
+abstract class DeviceDataStoreDataSource {
+
+ abstract suspend fun loadDeviceToken(): String
+
+ abstract suspend fun storeDeviceToken(deviceToken: String)
+
+ abstract suspend fun clearDeviceToken()
+}
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt
new file mode 100644
index 000000000..08b2633e7
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt
@@ -0,0 +1,17 @@
+package team.aliens.dms.android.core.device.datastore
+
+import team.aliens.dms.android.core.device.datastore.store.DeviceStore
+import team.aliens.dms.android.shared.exception.util.suspendRunCatching
+import javax.inject.Inject
+
+internal class DeviceDataStoreDataSourceImpl @Inject constructor(
+ private val deviceStore: DeviceStore,
+) : DeviceDataStoreDataSource() {
+ override suspend fun loadDeviceToken(): String = deviceStore.loadDeviceToken()
+
+ override suspend fun storeDeviceToken(deviceToken: String) =
+ deviceStore.storeDeviceToken(deviceToken)
+
+ override suspend fun clearDeviceToken() =
+ deviceStore.clearDeviceToken()
+}
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt
new file mode 100644
index 000000000..ac641c0e9
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt
@@ -0,0 +1,10 @@
+package team.aliens.dms.android.core.device.datastore.store
+
+internal abstract class DeviceStore {
+
+ abstract suspend fun loadDeviceToken(): String
+
+ abstract suspend fun storeDeviceToken(deviceToken: String)
+
+ abstract suspend fun clearDeviceToken()
+}
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt
new file mode 100644
index 000000000..32fb861ea
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt
@@ -0,0 +1,42 @@
+package team.aliens.dms.android.core.device.datastore.store
+
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.runBlocking
+import team.aliens.dms.android.core.datastore.DeviceDataStore
+import team.aliens.dms.android.core.datastore.PreferencesDataStore
+import team.aliens.dms.android.core.datastore.util.transform
+import team.aliens.dms.android.core.device.datastore.store.exception.CannotStoreDeviceTokenException
+import team.aliens.dms.android.core.device.datastore.store.exception.DeviceTokenNotFoundException
+import javax.inject.Inject
+
+internal class DeviceStoreImpl @Inject constructor(
+ @DeviceDataStore private val deviceDataStore: PreferencesDataStore,
+) : DeviceStore() {
+
+ override suspend fun loadDeviceToken(): String = deviceDataStore.data.map { preferences ->
+ preferences[DEVICE_TOKEN] ?: throw DeviceTokenNotFoundException()
+ }.first()
+
+ override suspend fun storeDeviceToken(deviceToken: String) {
+ transform(
+ onFailure = { throw CannotStoreDeviceTokenException() },
+ ) {
+ deviceDataStore.edit { preferences ->
+ preferences[DEVICE_TOKEN] = deviceToken
+ }
+ }
+ }
+
+ override suspend fun clearDeviceToken() {
+ transform {
+ deviceDataStore.edit { preferences -> preferences.clear() }
+ }
+ }
+
+ private companion object {
+ val DEVICE_TOKEN = stringPreferencesKey("device-token")
+ }
+}
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt
new file mode 100644
index 000000000..0a51c63e9
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt
@@ -0,0 +1,5 @@
+package team.aliens.dms.android.core.device.datastore.store.exception
+
+import team.aliens.dms.android.core.datastore.exception.TransformFailureException
+
+class CannotStoreDeviceTokenException : TransformFailureException("Cannot store deviceToken")
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt
new file mode 100644
index 000000000..10583752b
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt
@@ -0,0 +1,7 @@
+package team.aliens.dms.android.core.device.datastore.store.exception
+
+import team.aliens.dms.android.core.datastore.exception.LoadFailureException
+
+sealed class TokenNotFoundException(message: String?) : LoadFailureException(message)
+
+class DeviceTokenNotFoundException : TokenNotFoundException("Device token not found")
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/di/DataSourceModule.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/di/DataSourceModule.kt
new file mode 100644
index 000000000..3ecf4ae4d
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/di/DataSourceModule.kt
@@ -0,0 +1,18 @@
+package team.aliens.dms.android.core.device.di
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSource
+import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSourceImpl
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal abstract class DataSourceModule {
+
+ @Binds
+ @Singleton
+ abstract fun bindDeviceDataStoreDataSource(impl: DeviceDataStoreDataSourceImpl): DeviceDataStoreDataSource
+}
diff --git a/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/di/StoreModule.kt b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/di/StoreModule.kt
new file mode 100644
index 000000000..f5311733c
--- /dev/null
+++ b/core/device/src/prod/kotlin/team/aliens/dms/android/core/device/di/StoreModule.kt
@@ -0,0 +1,18 @@
+package team.aliens.dms.android.core.device.di
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import team.aliens.dms.android.core.device.datastore.store.DeviceStore
+import team.aliens.dms.android.core.device.datastore.store.DeviceStoreImpl
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal abstract class StoreModule {
+
+ @Binds
+ @Singleton
+ abstract fun bindDeviceStore(impl: DeviceStoreImpl): DeviceStore
+}
diff --git a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/mapper/NoticeMapper.kt b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/mapper/NoticeMapper.kt
index 6b13be2aa..0a4ae8e2e 100644
--- a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/mapper/NoticeMapper.kt
+++ b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/mapper/NoticeMapper.kt
@@ -1,7 +1,9 @@
package team.aliens.dms.android.data.notice.mapper
import team.aliens.dms.android.core.database.entity.NoticeEntity
+import team.aliens.dms.android.data.notice.model.LatestNotice
import team.aliens.dms.android.data.notice.model.Notice
+import team.aliens.dms.android.network.notice.model.FetchLatestNoticeResponse
import team.aliens.dms.android.network.notice.model.FetchNoticeDetailsResponse
import team.aliens.dms.android.network.notice.model.FetchNoticesResponse
import team.aliens.dms.android.shared.date.toLocalDateTime
@@ -31,3 +33,8 @@ internal fun FetchNoticeDetailsResponse.toModel(): Notice = Notice(
content = this.content,
createdAt = this.createdAt.toLocalDateTime(),
)
+
+internal fun FetchLatestNoticeResponse.toModel(): LatestNotice = LatestNotice(
+ id = this.id,
+ title = this.title,
+)
diff --git a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/model/LatestNotice.kt b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/model/LatestNotice.kt
new file mode 100644
index 000000000..61a980129
--- /dev/null
+++ b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/model/LatestNotice.kt
@@ -0,0 +1,8 @@
+package team.aliens.dms.android.data.notice.model
+
+import java.util.UUID
+
+data class LatestNotice(
+ val id: UUID? = null,
+ val title: String = "",
+)
diff --git a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepository.kt b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepository.kt
index 6fd773934..4a7b4528a 100644
--- a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepository.kt
+++ b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepository.kt
@@ -1,6 +1,8 @@
package team.aliens.dms.android.data.notice.repository
+import team.aliens.dms.android.data.notice.model.LatestNotice
import team.aliens.dms.android.data.notice.model.Notice
+import team.aliens.dms.android.network.notice.model.FetchLatestNoticeResponse
import team.aliens.dms.android.shared.model.Order
import java.util.UUID
@@ -11,4 +13,6 @@ abstract class NoticeRepository {
abstract suspend fun fetchNoticeDetails(noticeId: UUID): Result
abstract suspend fun fetchNotices(order: Order = Order.NEW): Result>
+
+ abstract suspend fun fetchLatestNotice(): Result
}
diff --git a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepositoryImpl.kt b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepositoryImpl.kt
index 9c44720cd..b707316fa 100644
--- a/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepositoryImpl.kt
+++ b/data/src/dev/kotlin/team.aliens.dms.android.data/notice/repository/NoticeRepositoryImpl.kt
@@ -1,6 +1,7 @@
package team.aliens.dms.android.data.notice.repository
import team.aliens.dms.android.data.notice.mapper.toModel
+import team.aliens.dms.android.data.notice.model.LatestNotice
import team.aliens.dms.android.data.notice.model.Notice
import team.aliens.dms.android.network.notice.datasource.NetworkNoticeDataSource
import team.aliens.dms.android.shared.model.Order
@@ -22,4 +23,8 @@ internal class NoticeRepositoryImpl @Inject constructor(
override suspend fun fetchNotices(order: Order): Result> =
networkNoticeDataSource.fetchNotices(order.name)
.map { it.toModel() }
+
+ override suspend fun fetchLatestNotice(): Result =
+ networkNoticeDataSource.fetchLatestNotice()
+ .map { it.toModel() }
}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/application/ui/ApplicationScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/application/ui/ApplicationScreen.kt
index 1420fb31b..7d125ac2c 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/application/ui/ApplicationScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/application/ui/ApplicationScreen.kt
@@ -1,13 +1,17 @@
package team.aliens.dms.android.feature.main.application.ui
+import android.R
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -15,10 +19,12 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.launch
import team.aliens.dms.android.core.designsystem.DmsTheme
+import team.aliens.dms.android.core.designsystem.bodyM
import team.aliens.dms.android.core.designsystem.snackbar.DmsSnackBarType
import team.aliens.dms.android.core.designsystem.tab.DmsTab
import team.aliens.dms.android.core.designsystem.tab.DmsTabRow
@@ -120,6 +126,17 @@ private fun ApplicationScreen(
onNavigateVolunteerApplication = onNavigateVolunteerApplication,
)
} else {
+ if (state.votes.isEmpty()) {
+ Text(
+ text = """
+ 예정된 투표가 없습니다.
+ 결과를 확인하시고 싶으시면 알림함을 확인해주세요!
+ """.trimIndent(),
+ style = DmsTheme.typography.bodyM,
+ color = DmsTheme.colorScheme.inverseSurface,
+ textAlign = TextAlign.Center,
+ )
+ }
VoteContent(
votes = state.votes,
onNavigateVote = onNavigateVote,
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/AnnouncementButton.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/AnnouncementButton.kt
index e5f1ec07a..c610228a4 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/AnnouncementButton.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/AnnouncementButton.kt
@@ -23,6 +23,7 @@ import team.aliens.dms.android.core.designsystem.util.clickable
@Composable
internal fun AnnouncementButton(
modifier: Modifier = Modifier,
+ title: String,
onClick: () -> Unit,
) {
Row(
@@ -47,7 +48,7 @@ internal fun AnnouncementButton(
)
Text(
modifier = Modifier.padding(start = 8.dp),
- text = "새로운 공지사항을 확인하세요",
+ text = title,
style = DmsTheme.typography.labelM,
color = DmsTheme.colorScheme.inverseSurface,
)
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/DmsPointCotent.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/DmsPointCotent.kt
index 71368be15..151182c6e 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/DmsPointCotent.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/model/DmsPointCotent.kt
@@ -25,7 +25,7 @@ import team.aliens.dms.android.core.designsystem.foundation.DmsIcon
@Composable
fun DmsPointContent(
modifier: Modifier = Modifier,
- plusPoint: Int,
+ bonusPoint: Int,
minusPoint: Int,
) {
Column(
@@ -49,7 +49,7 @@ fun DmsPointContent(
buttonColor = DmsTheme.colorScheme.onSurface,
icon = DmsIcon.Equal,
title = "총점",
- point = plusPoint - minusPoint,
+ point = bonusPoint - minusPoint,
)
PointItem(
modifier = Modifier.padding(top = 12.dp),
@@ -57,7 +57,7 @@ fun DmsPointContent(
buttonColor = DmsTheme.colorScheme.primary,
icon = DmsIcon.Plus,
title = "상점",
- point = plusPoint,
+ point = bonusPoint,
)
PointItem(
modifier = Modifier.padding(top = 12.dp),
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/ui/HomeScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/ui/HomeScreen.kt
index ed78a6b77..2c540a99c 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/ui/HomeScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/ui/HomeScreen.kt
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -56,6 +57,14 @@ internal fun Home(
DmsSnackBarType.SUCCESS,
"개발중인 기능이에요",
)
+ is HomeSideEffect.FailFetchMyPage -> onShowSnackBar(
+ DmsSnackBarType.ERROR,
+ effect.message,
+ )
+ is HomeSideEffect.FailFetchLatestNotice -> onShowSnackBar(
+ DmsSnackBarType.ERROR,
+ effect.message,
+ )
}
}
}
@@ -85,8 +94,7 @@ private fun HomeScreen(
modifier = Modifier
.fillMaxSize()
.background(DmsTheme.colorScheme.background)
- .systemBarsPadding()
- .navigationBarsPadding(),
+ .statusBarsPadding(),
) {
HomeTopAppBar(
onOutingPassClick = onOutingPassClick,
@@ -102,7 +110,8 @@ private fun HomeScreen(
modifier = Modifier
.fillMaxWidth()
.padding(start = 4.dp),
- onClick = { onNavigateNoticeDetail(state.noticeId) },
+ title = state.latestNotice.title,
+ onClick = { onNavigateNoticeDetail(state.latestNotice.id) },
)
MealContent(
modifier = Modifier
@@ -115,7 +124,7 @@ private fun HomeScreen(
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp),
- plusPoint = state.myPage.bonusPoint,
+ bonusPoint = state.myPage.bonusPoint,
minusPoint = state.myPage.minusPoint,
)
DmsItemButton(
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/viewmodel/HomeViewModel.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/viewmodel/HomeViewModel.kt
index 94fdbeca9..1e6108b88 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/viewmodel/HomeViewModel.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/home/viewmodel/HomeViewModel.kt
@@ -5,6 +5,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import team.aliens.dms.android.core.ui.viewmodel.BaseStateViewModel
+import team.aliens.dms.android.data.notice.model.LatestNotice
+import team.aliens.dms.android.data.notice.repository.NoticeRepository
import team.aliens.dms.android.data.student.model.MyPage
import team.aliens.dms.android.data.student.repository.StudentRepository
import java.util.UUID
@@ -13,10 +15,12 @@ import javax.inject.Inject
@HiltViewModel
internal class HomeViewModel @Inject constructor(
val studentRepository: StudentRepository,
+ val noticeRepository: NoticeRepository
) : BaseStateViewModel(HomeState()) {
init {
getMyPage()
+ getLatestNotice()
}
private fun getMyPage() {
@@ -24,7 +28,19 @@ internal class HomeViewModel @Inject constructor(
studentRepository.fetchMyPage().onSuccess { myPage ->
setState { it.copy(myPage = myPage) }
}.onFailure {
- throw it
+ sendEffect(HomeSideEffect.FailFetchMyPage("내 정보 조회를 실패했어요"))
+ }
+ }
+ }
+
+ private fun getLatestNotice() {
+ viewModelScope.launch(Dispatchers.IO) {
+ noticeRepository.fetchLatestNotice().onSuccess { latestNotice ->
+ setState {
+ it.copy(latestNotice = latestNotice)
+ }
+ }.onFailure {
+ sendEffect(HomeSideEffect.FailFetchLatestNotice("공지를 가져오는데 실패했어요"))
}
}
}
@@ -35,11 +51,14 @@ internal class HomeViewModel @Inject constructor(
}
internal data class HomeState(
- val newNoticesExist: Boolean = false,
val myPage: MyPage = MyPage(),
- val noticeId: UUID? = null
+ val latestNotice: LatestNotice = LatestNotice(),
)
internal sealed interface HomeSideEffect {
data object ShowOutingPassDialog : HomeSideEffect
+
+ data class FailFetchLatestNotice(val message: String) : HomeSideEffect
+
+ data class FailFetchMyPage(val message: String) : HomeSideEffect
}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/mypage/ui/MyPageScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/mypage/ui/MyPageScreen.kt
index f6ee823bb..e29aff389 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/mypage/ui/MyPageScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/main/mypage/ui/MyPageScreen.kt
@@ -101,7 +101,7 @@ private fun MyPageScreen(
)
DmsPointContent(
modifier = Modifier,
- plusPoint = state.myPage.bonusPoint,
+ bonusPoint = state.myPage.bonusPoint,
minusPoint = state.myPage.minusPoint,
)
DmsItemButton(
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notice/ui/NoticeDetailScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notice/ui/NoticeDetailScreen.kt
index aba82e61c..96fdba7c3 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notice/ui/NoticeDetailScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notice/ui/NoticeDetailScreen.kt
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -105,7 +106,7 @@ private fun NotificationDetailContent(
.horizontalPadding(10.dp)
.background(color = DmsTheme.colorScheme.surfaceTint, shape = RoundedCornerShape(32.dp))
.padding(vertical = 24.dp, horizontal = 32.dp)
- .horizontalScroll(rememberScrollState()),
+ .verticalScroll(rememberScrollState()),
) {
Text(
text = notice.title,
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/navigation/NotificationRoute.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/navigation/NotificationRoute.kt
index 7064b9fb8..ac1ee1899 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/navigation/NotificationRoute.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/navigation/NotificationRoute.kt
@@ -3,7 +3,7 @@ package team.aliens.dms.android.feature.notification.navigation
import androidx.compose.runtime.Composable
import team.aliens.dms.android.core.designsystem.snackbar.DmsSnackBarType
import team.aliens.dms.android.data.point.model.PointType
-import team.aliens.dms.android.feature.notification.ui.NotificationScreen
+import team.aliens.dms.android.feature.notification.ui.Notification
import java.util.UUID
@Composable
@@ -13,10 +13,10 @@ fun NotificationRoute(
onNavigatePointHistory: (PointType) -> Unit,
onShowSnackBar: (DmsSnackBarType, String) -> Unit,
) {
- NotificationScreen(
+ Notification(
onBackClick = onBackClick,
onNavigateNotificationDetailClick = onNavigateNotificationDetailClick,
onNavigatePointHistory = onNavigatePointHistory,
- onShowSnackBar = onShowSnackBar
+ onShowSnackBar = onShowSnackBar,
)
}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/NotificationScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/NotificationScreen.kt
index 635d1a722..0dbaf980b 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/NotificationScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/NotificationScreen.kt
@@ -44,6 +44,7 @@ import team.aliens.dms.android.core.designsystem.tab.DmsTabRow
import team.aliens.dms.android.core.designsystem.topPadding
import team.aliens.dms.android.core.designsystem.util.clickable
import team.aliens.dms.android.data.point.model.PointType
+import team.aliens.dms.android.feature.notification.ui.component.NoticeItem
import team.aliens.dms.android.feature.notification.viewmodel.NotificationSideEffect
import team.aliens.dms.android.feature.notification.viewmodel.NotificationViewModel
import team.aliens.dms.android.feature.notification.viewmodel.NotificationState
@@ -51,7 +52,7 @@ import team.aliens.dms.android.feature.notification.viewmodel.NotificationUi
import java.util.UUID
@Composable
-internal fun NotificationScreen(
+internal fun Notification(
onBackClick: () -> Unit,
onNavigateNotificationDetailClick: (UUID) -> Unit,
onNavigatePointHistory: (PointType) -> Unit,
@@ -83,7 +84,7 @@ internal fun NotificationScreen(
}
}
- NotificationScreenContent(
+ NotificationScreen(
state = state,
tabData = tabData.toPersistentList(),
pagerState = pagerState,
@@ -106,7 +107,7 @@ internal fun NotificationScreen(
}
@Composable
-private fun NotificationScreenContent(
+private fun NotificationScreen(
state: NotificationState,
tabData: ImmutableList,
pagerState: PagerState,
@@ -185,7 +186,7 @@ private fun NoticeItems(
onNotificationDetailClick: (UUID, UUID) -> Unit,
) {
LazyColumn(
- modifier = modifier.fillMaxWidth(),
+ modifier = modifier.fillMaxSize(),
) {
items(
items = notices,
@@ -256,63 +257,3 @@ internal fun NotificationItem(
)
}
}
-
-
-@Composable
-internal fun NoticeItem(
- modifier: Modifier = Modifier,
- notice: NotificationUi,
- onNotificationDetailClick: (UUID, UUID) -> Unit,
-) {
- Row(
- modifier = modifier
- .fillMaxWidth()
- .clickable(onClick = { onNotificationDetailClick(notice.linkId, notice.id) })
- .padding(horizontal = 24.dp, vertical = 22.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- painter = painterResource(DmsIcon.Notice),
- tint = DmsTheme.colorScheme.scrim,
- contentDescription = null,
- )
- Column(
- modifier = modifier.startPadding(12.dp),
- ) {
- Text(
- text = notice.title,
- style = DmsTheme.typography.bodyM,
- )
- Row(
- modifier = modifier.topPadding(6.dp)
- ) {
- if (!notice.isRead) {
- Icon(
- modifier = modifier.size(4.dp),
- imageVector = Icons.Filled.Circle,
- contentDescription = null,
- tint = DmsTheme.colorScheme.primaryContainer,
- )
- }
- Text(
- modifier = modifier
- .startPadding(4.dp),
- text = notice.content,
- style = DmsTheme.typography.labelM,
- )
- }
- }
- Spacer(modifier = modifier.weight(1f))
- Text(
- modifier = Modifier.padding(horizontal = 10.dp),
- text = notice.elapsedText,
- style = DmsTheme.typography.bodyM,
- color = DmsTheme.colorScheme.inverseSurface,
- )
- Icon(
- painter = painterResource(DmsIcon.Forward),
- tint = DmsTheme.colorScheme.scrim,
- contentDescription = null,
- )
- }
-}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/component/NoticeItem.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/component/NoticeItem.kt
new file mode 100644
index 000000000..05aaaef6d
--- /dev/null
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/notification/ui/component/NoticeItem.kt
@@ -0,0 +1,89 @@
+package team.aliens.dms.android.feature.notification.ui.component
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Circle
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import team.aliens.dms.android.core.designsystem.DmsTheme
+import team.aliens.dms.android.core.designsystem.bodyM
+import team.aliens.dms.android.core.designsystem.foundation.DmsIcon
+import team.aliens.dms.android.core.designsystem.labelM
+import team.aliens.dms.android.core.designsystem.startPadding
+import team.aliens.dms.android.core.designsystem.topPadding
+import team.aliens.dms.android.core.designsystem.util.clickable
+import team.aliens.dms.android.feature.notification.viewmodel.NotificationUi
+import java.util.UUID
+
+@Composable
+internal fun NoticeItem(
+ modifier: Modifier = Modifier,
+ notice: NotificationUi,
+ onNotificationDetailClick: (UUID, UUID) -> Unit,
+) {
+ Column(
+ modifier = modifier
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(onClick = { onNotificationDetailClick(notice.linkId, notice.id) })
+ .padding(horizontal = 24.dp, vertical = 22.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ painter = painterResource(DmsIcon.Notice),
+ tint = DmsTheme.colorScheme.scrim,
+ contentDescription = null,
+ )
+ Column(
+ modifier = Modifier.startPadding(12.dp),
+ ) {
+ Text(
+ text = notice.title,
+ style = DmsTheme.typography.bodyM,
+ )
+ Row(
+ modifier = Modifier.topPadding(6.dp)
+ ) {
+ if (!notice.isRead) {
+ Icon(
+ modifier = Modifier.size(4.dp),
+ imageVector = Icons.Filled.Circle,
+ contentDescription = null,
+ tint = DmsTheme.colorScheme.primaryContainer,
+ )
+ }
+ Text(
+ modifier = Modifier
+ .startPadding(4.dp),
+ text = notice.content,
+ style = DmsTheme.typography.labelM,
+ )
+ }
+ }
+ Spacer(modifier = Modifier.weight(1f))
+ Text(
+ modifier = Modifier.padding(horizontal = 10.dp),
+ text = notice.elapsedText,
+ style = DmsTheme.typography.bodyM,
+ color = DmsTheme.colorScheme.inverseSurface,
+ )
+ Icon(
+ painter = painterResource(DmsIcon.Forward),
+ tint = DmsTheme.colorScheme.scrim,
+ contentDescription = null,
+ )
+ }
+ }
+}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/ui/PointHistoryScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/ui/PointHistoryScreen.kt
index 26d4c95de..8ff542d4b 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/ui/PointHistoryScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/ui/PointHistoryScreen.kt
@@ -62,7 +62,7 @@ private fun PointHistoryScreen(
)
val tabData = listOf(
PointTab.All,
- PointTab.BONUS,
+ PointTab.Bonus,
PointTab.Minus,
)
val pagerState = rememberPagerState(
@@ -99,7 +99,7 @@ private fun PointHistoryScreen(
val pointHistoryList = remember(page, state.allPointList) {
when (tabData[page]) {
PointTab.All -> state.allPointList
- PointTab.BONUS -> state.plusPointList
+ PointTab.Bonus -> state.bonusPointList
PointTab.Minus -> state.minusPointList
}
}
@@ -129,6 +129,6 @@ internal sealed class PointTab(
val pointType: PointType,
) {
data object All : PointTab(title = "전체", pointType = PointType.ALL)
- data object BONUS : PointTab(title = "상점", pointType = PointType.BONUS)
+ data object Bonus : PointTab(title = "상점", pointType = PointType.BONUS)
data object Minus : PointTab(title = "벌점", pointType = PointType.MINUS)
}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/viewmodel/PointHistoryViewModel.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/viewmodel/PointHistoryViewModel.kt
index ec6240fef..31b9e58e2 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/viewmodel/PointHistoryViewModel.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/point/viewmodel/PointHistoryViewModel.kt
@@ -32,7 +32,7 @@ internal class PointHistoryViewModel @Inject constructor(
setState {
it.copy(
allPointList = pointHistories,
- plusPointList = bonusPoints,
+ bonusPointList = bonusPoints,
minusPointList = minusPoints,
)
}
@@ -47,7 +47,7 @@ internal class PointHistoryViewModel @Inject constructor(
internal data class PointHistoryState(
val allPointList: List = emptyList(),
- val plusPointList: List = emptyList(),
+ val bonusPointList: List = emptyList(),
val minusPointList: List = emptyList(),
val initialTab: Int = 0,
)
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/resetpassword/viewmodel/ResetPasswordViewModel.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/resetpassword/viewmodel/ResetPasswordViewModel.kt
index 6fe40c90a..6c063cfb6 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/resetpassword/viewmodel/ResetPasswordViewModel.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/resetpassword/viewmodel/ResetPasswordViewModel.kt
@@ -10,7 +10,7 @@ import javax.inject.Inject
@HiltViewModel
class ResetPasswordViewModel @Inject constructor(
- private val userRepository: UserRepository
+ private val userRepository: UserRepository,
): BaseStateViewModel(ResetPasswordState()) {
internal fun setNewPassword(password: String) {
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/ui/SettingScreen.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/ui/SettingScreen.kt
index 5f76b40a5..61fc97c1e 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/ui/SettingScreen.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/ui/SettingScreen.kt
@@ -27,8 +27,6 @@ import team.aliens.dms.android.core.designsystem.button.DmsItemButton
import team.aliens.dms.android.core.designsystem.dialog.AlertDialog
import team.aliens.dms.android.core.designsystem.sTitleM
import team.aliens.dms.android.core.designsystem.snackbar.DmsSnackBarType
-import team.aliens.dms.android.core.designsystem.titleB
-import team.aliens.dms.android.core.designsystem.titleM
import team.aliens.dms.android.feature.setting.ui.component.SettingRotateContent
import team.aliens.dms.android.feature.setting.viewmodel.SettingSideEffect
import team.aliens.dms.android.feature.setting.viewmodel.SettingViewModel
@@ -46,14 +44,19 @@ internal fun Setting(
val (shouldShowSignOutDialog, onShouldShowSignOutDialogChange) = remember {
mutableStateOf(false)
}
+ val (shouldShowWithdrawDialog, onShouldShowWithdrawDialogChange) = remember {
+ mutableStateOf(false)
+ }
LaunchedEffect(Unit) {
viewModel.sideEffect.collect {
when (it) {
- SettingSideEffect.CannotFetchNotificationStatus -> {
+ is SettingSideEffect.CannotFetchNotificationStatus ->
onShowSnackBar(DmsSnackBarType.ERROR, "알림 상태 조회를 실패했어요")
- }
- SettingSideEffect.SignOutSuccess -> onNavigateSignIn()
+ is SettingSideEffect.SignOutSuccess -> onNavigateSignIn()
+ is SettingSideEffect.WithdrawSuccess -> onNavigateSignIn()
+ is SettingSideEffect.WithdrawFailed ->
+ onShowSnackBar(DmsSnackBarType.ERROR, "회원 탈퇴에 실패했어요")
}
}
}
@@ -82,12 +85,37 @@ internal fun Setting(
)
}
+ if (shouldShowWithdrawDialog) {
+ AlertDialog(
+ title = { Text(text = "회원 탈퇴", style = DmsTheme.typography.sTitleM) },
+ text = { Text(text = "회원 탈퇴 시 복구할 수 없습니다. 정말 탈퇴하시겠습니까?", style = DmsTheme.typography.bodyM) },
+ onDismissRequest = { onShouldShowWithdrawDialogChange(false) },
+ confirmButton = {
+ DmsButton(
+ text = "확인",
+ buttonType = ButtonType.Text,
+ buttonColor = ButtonColor.Primary,
+ onClick = viewModel::withdraw,
+ )
+ },
+ dismissButton = {
+ DmsButton(
+ text = "취소",
+ buttonType = ButtonType.Text,
+ buttonColor = ButtonColor.Primary,
+ onClick = { onShouldShowWithdrawDialogChange(false) },
+ )
+ },
+ )
+ }
+
SettingScreen(
rotated = state.isOnNotification,
onNavigateResetPassword = onNavigateResetPassword,
onNavigateSelectProfile = onNavigateSelectProfile,
onNotificationClick = { viewModel.updateNotificationStatus(state.isOnNotification) },
onShowSignOutDialogChange = { onShouldShowSignOutDialogChange(true) },
+ onShowWithdrawDialogChange = { onShouldShowWithdrawDialogChange(true) },
onBackPressed = onBackPressed,
)
}
@@ -99,6 +127,7 @@ private fun SettingScreen(
onNavigateSelectProfile: () -> Unit,
onNotificationClick: () -> Unit,
onShowSignOutDialogChange: () -> Unit,
+ onShowWithdrawDialogChange: () -> Unit,
onBackPressed: () -> Unit,
) {
Column(
@@ -137,6 +166,11 @@ private fun SettingScreen(
text = "로그아웃",
onClick = onShowSignOutDialogChange,
)
+ DmsItemButton(
+ iconRes = R.drawable.img_3d_out,
+ text = "회원 탈퇴",
+ onClick = onShowWithdrawDialogChange,
+ )
}
}
}
diff --git a/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/viewmodel/SettingViewModel.kt b/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/viewmodel/SettingViewModel.kt
index 7a54eb85d..7da6bae6b 100644
--- a/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/viewmodel/SettingViewModel.kt
+++ b/feature/src/dev/kotlin/team/aliens/dms/android/feature/setting/viewmodel/SettingViewModel.kt
@@ -8,6 +8,7 @@ import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSource
import team.aliens.dms.android.core.ui.viewmodel.BaseStateViewModel
import team.aliens.dms.android.data.auth.repository.AuthRepository
import team.aliens.dms.android.data.notification.model.NotificationTopicGroup
+import team.aliens.dms.android.data.student.repository.StudentRepository
import team.aliens.dms.android.data.notification.repository.NotificationRepository
import javax.inject.Inject
@@ -16,6 +17,7 @@ class SettingViewModel @Inject constructor(
val notificationRepository: NotificationRepository,
val authRepository: AuthRepository,
val deviceDataStoreDataSource: DeviceDataStoreDataSource,
+ val studentRepository: StudentRepository,
): BaseStateViewModel(SettingState()) {
init {
@@ -43,6 +45,14 @@ class SettingViewModel @Inject constructor(
}
}
+ internal fun withdraw() {
+ viewModelScope.launch(Dispatchers.IO) {
+ studentRepository.withdraw()
+ .onSuccess { sendEffect(SettingSideEffect.WithdrawSuccess) }
+ .onFailure { sendEffect(SettingSideEffect.WithdrawFailed) }
+ }
+ }
+
private fun fetchNotificationStatus() {
viewModelScope.launch {
val deviceToken = uiState.value.deviceToken ?: return@launch
@@ -80,4 +90,6 @@ data class SettingState(
sealed class SettingSideEffect {
object CannotFetchNotificationStatus : SettingSideEffect()
object SignOutSuccess : SettingSideEffect()
+ object WithdrawSuccess : SettingSideEffect()
+ object WithdrawFailed : SettingSideEffect()
}
diff --git a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/apiservice/NoticeApiService.kt b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/apiservice/NoticeApiService.kt
index c4267c523..99c51114b 100644
--- a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/apiservice/NoticeApiService.kt
+++ b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/apiservice/NoticeApiService.kt
@@ -4,6 +4,7 @@ import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
import team.aliens.dms.android.core.jwt.RequiresAccessToken
+import team.aliens.dms.android.network.notice.model.FetchLatestNoticeResponse
import team.aliens.dms.android.network.notice.model.FetchNoticeDetailsResponse
import team.aliens.dms.android.network.notice.model.FetchNoticesResponse
import team.aliens.dms.android.network.notice.model.FetchWhetherNewNoticesExistResponse
@@ -22,4 +23,8 @@ internal interface NoticeApiService {
@GET("/notices")
@RequiresAccessToken
suspend fun fetchNotices(@Query("order") order: String): FetchNoticesResponse
+
+ @GET("/notices/latest")
+ @RequiresAccessToken
+ suspend fun fetchLatestNotice(): FetchLatestNoticeResponse
}
diff --git a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSource.kt b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSource.kt
index fe5133329..f114c7f16 100644
--- a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSource.kt
+++ b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSource.kt
@@ -1,5 +1,6 @@
package team.aliens.dms.android.network.notice.datasource
+import team.aliens.dms.android.network.notice.model.FetchLatestNoticeResponse
import team.aliens.dms.android.network.notice.model.FetchNoticeDetailsResponse
import team.aliens.dms.android.network.notice.model.FetchNoticesResponse
import team.aliens.dms.android.network.notice.model.FetchWhetherNewNoticesExistResponse
@@ -12,4 +13,6 @@ abstract class NetworkNoticeDataSource {
abstract suspend fun fetchNotices(order: String): Result
abstract suspend fun fetchNoticeDetails(noticeId: UUID): Result
+
+ abstract suspend fun fetchLatestNotice(): Result
}
diff --git a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSourceImpl.kt b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSourceImpl.kt
index c43e4120b..a4916b1a0 100644
--- a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSourceImpl.kt
+++ b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/datasource/NetworkNoticeDataSourceImpl.kt
@@ -1,5 +1,6 @@
package team.aliens.dms.android.network.notice.datasource
import team.aliens.dms.android.network.notice.apiservice.NoticeApiService
+import team.aliens.dms.android.network.notice.model.FetchLatestNoticeResponse
import team.aliens.dms.android.network.notice.model.FetchNoticeDetailsResponse
import team.aliens.dms.android.network.notice.model.FetchNoticesResponse
import team.aliens.dms.android.network.notice.model.FetchWhetherNewNoticesExistResponse
@@ -18,4 +19,7 @@ internal class NetworkNoticeDataSourceImpl @Inject constructor(
override suspend fun fetchNoticeDetails(noticeId: UUID): Result =
suspendRunCatching { noticeApiService.fetchNoticeDetails(noticeId) }
+
+ override suspend fun fetchLatestNotice(): Result =
+ suspendRunCatching { noticeApiService.fetchLatestNotice() }
}
diff --git a/network/src/dev/kotlin/team.aliens.dms.android.network/notice/model/FetchLatestNoticeResponse.kt b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/model/FetchLatestNoticeResponse.kt
new file mode 100644
index 000000000..2cad70e7c
--- /dev/null
+++ b/network/src/dev/kotlin/team.aliens.dms.android.network/notice/model/FetchLatestNoticeResponse.kt
@@ -0,0 +1,9 @@
+package team.aliens.dms.android.network.notice.model
+
+import com.google.gson.annotations.SerializedName
+import java.util.UUID
+
+data class FetchLatestNoticeResponse(
+ @SerializedName("id") val id: UUID,
+ @SerializedName("title") val title: String,
+)
diff --git a/network/src/dev/kotlin/team.aliens.dms.android.network/notification/model/NotificationTopicGroup.kt b/network/src/dev/kotlin/team.aliens.dms.android.network/notification/model/NotificationTopicGroup.kt
deleted file mode 100644
index c144aafe5..000000000
--- a/network/src/dev/kotlin/team.aliens.dms.android.network/notification/model/NotificationTopicGroup.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package team.aliens.dms.android.network.notification.model
-
-enum class NotificationTopicGroup {
- NOTICE, STUDY_ROOM,
- ;
-}