diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 9067f16..3d9c95a 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -35,9 +35,15 @@ jobs:
- name: Build with Gradle
run: ./gradlew assembleDebug
+ env:
+ BASE_URL: ${{ secrets.BASE_URL }}
+ KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
- name: Run unit tests
run: ./gradlew test
+ env:
+ BASE_URL: ${{ secrets.BASE_URL }}
+ KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
- name: Upload test reports
if: always()
@@ -87,4 +93,7 @@ jobs:
run: chmod +x gradlew
- name: Run Fastlane distribute
- run: bundle exec fastlane distribute
\ No newline at end of file
+ run: bundle exec fastlane distribute
+ env:
+ BASE_URL: ${{ secrets.BASE_URL }}
+ KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
index e5f298b..37bf1cf 100644
--- a/.idea/caches/deviceStreaming.xml
+++ b/.idea/caches/deviceStreaming.xml
@@ -51,18 +51,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -765,6 +753,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -887,6 +943,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -975,6 +1171,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -987,6 +1217,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 59f7d49..0bbfe68 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,4 +1,5 @@
import java.util.Properties
+import org.gradle.api.GradleException
plugins {
alias(libs.plugins.android.application)
@@ -17,6 +18,21 @@ val properties = Properties().apply {
load(localPropertiesFile.inputStream())
}
}
+
+fun getRequiredProperty(key: String): String {
+
+ val localValue = properties[key]?.toString()
+ if (!localValue.isNullOrBlank()) {
+ return localValue
+ }
+
+ val envValue = System.getenv(key.toUpperCase().replace('.', '_'))
+ if (!envValue.isNullOrBlank()) {
+ return envValue
+ }
+
+ throw GradleException("Property '$key' is missing. Please define it in local.properties or as an environment variable (e.g., ${key.toUpperCase().replace('.', '_')}) in CI/CD.")
+}
android {
namespace = "com.hsLink.hslink"
compileSdk = libs.versions.compileSdk.get().toInt()
@@ -30,7 +46,8 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- buildConfigField("String", "BASE_URL", "\"${properties["base.url"]}\"")
+ buildConfigField("String", "BASE_URL", "\"${getRequiredProperty("base.url")}\"")
+ manifestPlaceholders["kakaoAppKey"] = getRequiredProperty("kakao.native.app.key")
}
@@ -41,7 +58,7 @@ android {
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
- buildConfigField("String", "BASE_URL", "\"${properties["base.url"]}\"")
+ buildConfigField("String", "BASE_URL", "\"${getRequiredProperty("base.url")}\"")
}
}
compileOptions {
@@ -91,4 +108,11 @@ dependencies {
// KaKao
implementation("com.kakao.sdk:v2-user:2.20.1")
+ // DataStore
+ implementation("androidx.datastore:datastore-preferences:1.1.1")
+
+ // Paging3
+ implementation("androidx.paging:paging-runtime-ktx:3.3.0")
+ implementation("androidx.paging:paging-compose:3.3.0")
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 94bbce5..2ae4b22 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,7 +36,7 @@
-
+
diff --git a/app/src/main/java/com/hsLink/hslink/data/di/DataStoreModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/DataStoreModule.kt
new file mode 100644
index 0000000..e602220
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/di/DataStoreModule.kt
@@ -0,0 +1,21 @@
+package com.hsLink.hslink.data.di
+
+import android.content.Context
+import com.hsLink.hslink.data.local.TokenDataStore
+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 provideTokenDataStore(@ApplicationContext context: Context): TokenDataStore {
+ return TokenDataStore(context)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
index d434a4b..36ed472 100644
--- a/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
@@ -1,6 +1,7 @@
package com.hsLink.hslink.data.di
import com.hsLink.hslink.BuildConfig
+import com.hsLink.hslink.data.remote.AuthInterceptor
import com.hsLink.hslink.data.service.commuunity.CommunityPostService
import com.hsLink.hslink.data.service.home.PostService
import com.hsLink.hslink.data.service.login.AuthService
@@ -13,9 +14,7 @@ import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
-import retrofit2.Converter
import retrofit2.Retrofit
-import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@Module
@@ -24,54 +23,46 @@ object NetworkModule {
@Provides
@Singleton
- fun providesLoggingInterceptor() = HttpLoggingInterceptor().apply {
- level = if (BuildConfig.DEBUG) {
- HttpLoggingInterceptor.Level.BODY
- } else {
- HttpLoggingInterceptor.Level.NONE
- }
+ fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
+ return OkHttpClient.Builder()
+ .addInterceptor(authInterceptor)
+ .addInterceptor(
+ HttpLoggingInterceptor().apply {
+ level = if (BuildConfig.DEBUG) {
+ HttpLoggingInterceptor.Level.BODY
+ } else {
+ HttpLoggingInterceptor.Level.NONE
+ }
+ }
+ )
+ .build()
}
@Provides
@Singleton
- fun providesOkHttpClient(
- loggingInterceptor: HttpLoggingInterceptor,
- ): OkHttpClient = OkHttpClient.Builder()
- .connectTimeout(10, TimeUnit.SECONDS)
- .readTimeout(10, TimeUnit.SECONDS)
- .addInterceptor(loggingInterceptor)
- .build()
-
- @Provides
- @Singleton
- fun providesConverterFactory(): Converter.Factory =
- Json.Default.asConverterFactory("application/json".toMediaType())
-
- @Provides
- @Singleton
- fun providesRetrofit(
- client: OkHttpClient,
- converterFactory: Converter.Factory,
- ): Retrofit {
+ fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
- .addConverterFactory(converterFactory)
- .client(client)
+ .client(okHttpClient)
+ .addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
}
@Provides
@Singleton
- fun providePostService(retrofit: Retrofit): PostService =
- retrofit.create(PostService::class.java)
+ fun provideCommunityPostService(retrofit: Retrofit): CommunityPostService {
+ return retrofit.create(CommunityPostService::class.java)
+ }
@Provides
@Singleton
- fun provideAuthService(retrofit: Retrofit): AuthService {
- return retrofit.create(AuthService::class.java)
+ fun providePostService(retrofit: Retrofit): PostService {
+ return retrofit.create(PostService::class.java)
}
+
@Provides
@Singleton
- fun provideCommunityPostService(retrofit: Retrofit): CommunityPostService =
- retrofit.create(CommunityPostService::class.java)
+ fun provideAuthService(retrofit: Retrofit): AuthService {
+ return retrofit.create(AuthService::class.java)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequestDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/SocialLoginRequestDto.kt
similarity index 75%
rename from app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequestDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/request/auth/SocialLoginRequestDto.kt
index fe22c4e..88200fc 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequestDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/SocialLoginRequestDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.request
+package com.hsLink.hslink.data.dto.request.auth
import kotlinx.serialization.Serializable
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/PostRequestDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/community/PostRequestDto.kt
similarity index 83%
rename from app/src/main/java/com/hsLink/hslink/data/dto/request/PostRequestDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/request/community/PostRequestDto.kt
index b861d1e..efac9a9 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/request/PostRequestDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/community/PostRequestDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.request
+package com.hsLink.hslink.data.dto.request.community
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -11,4 +11,4 @@ data class PostRequestDto (
val title : String,
@SerialName("body")
val body : String
-)
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/auth/SocialLoginResponseDto.kt
similarity index 81%
rename from app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponseDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/response/auth/SocialLoginResponseDto.kt
index b1ef367..2904d42 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponseDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/auth/SocialLoginResponseDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.response
+package com.hsLink.hslink.data.dto.response.auth
import kotlinx.serialization.Serializable
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityListResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityListResponseDto.kt
new file mode 100644
index 0000000..ea4ba4d
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityListResponseDto.kt
@@ -0,0 +1,32 @@
+package com.hsLink.hslink.data.dto.response.community
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class CommunityListResponseDto(
+ @SerialName("posts")
+ val posts: List,
+ @SerialName("currentPage")
+ val currentPage: Int,
+ @SerialName("hasNext")
+ val hasNext: Boolean,
+ @SerialName("totalElements")
+ val totalElements: Int,
+)
+
+@Serializable
+data class CommunityListDto(
+ @SerialName("id")
+ val id: Int,
+ @SerialName("title")
+ val title: String,
+ @SerialName("summary")
+ val summary: String,
+ @SerialName("author")
+ val author: String,
+ @SerialName("studentId")
+ val studentId: String,
+ @SerialName("authorStatus")
+ val authorStatus: String,
+)
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/CommunityPostResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityPostResponseDto.kt
similarity index 88%
rename from app/src/main/java/com/hsLink/hslink/data/dto/response/CommunityPostResponseDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityPostResponseDto.kt
index 3238825..798d23d 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/response/CommunityPostResponseDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityPostResponseDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.response
+package com.hsLink.hslink.data.dto.response.community
import com.hsLink.hslink.domain.model.community.CommunityPostResponseEntity
import kotlinx.serialization.SerialName
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/PostResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/home/PostResponseDto.kt
similarity index 96%
rename from app/src/main/java/com/hsLink/hslink/data/dto/response/PostResponseDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/response/home/PostResponseDto.kt
index e422bdc..c51fe5a 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/response/PostResponseDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/home/PostResponseDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.response
+package com.hsLink.hslink.data.dto.response.home
import com.hsLink.hslink.domain.model.home.PostPopularEntity
import com.hsLink.hslink.domain.model.home.PostPromotionEntity
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/UserProfileResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/UserProfileResponseDto.kt
similarity index 94%
rename from app/src/main/java/com/hsLink/hslink/data/dto/response/UserProfileResponseDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/UserProfileResponseDto.kt
index 6ebf15c..64dbcc1 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/response/UserProfileResponseDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/UserProfileResponseDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.response
+package com.hsLink.hslink.data.dto.response.mypage
import kotlinx.serialization.Serializable
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/SearchResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/search/SearchResponseDto.kt
similarity index 91%
rename from app/src/main/java/com/hsLink/hslink/data/dto/response/SearchResponseDto.kt
rename to app/src/main/java/com/hsLink/hslink/data/dto/response/search/SearchResponseDto.kt
index b5c6e88..b1f0de1 100644
--- a/app/src/main/java/com/hsLink/hslink/data/dto/response/SearchResponseDto.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/search/SearchResponseDto.kt
@@ -1,4 +1,4 @@
-package com.hsLink.hslink.data.dto.response
+package com.hsLink.hslink.data.dto.response.search
import kotlinx.serialization.Serializable
diff --git a/app/src/main/java/com/hsLink/hslink/data/local/TokenDataStore.kt b/app/src/main/java/com/hsLink/hslink/data/local/TokenDataStore.kt
new file mode 100644
index 0000000..d1698d8
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/local/TokenDataStore.kt
@@ -0,0 +1,42 @@
+package com.hsLink.hslink.data.local
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+class TokenDataStore @Inject constructor(private val context: Context) {
+
+ private val Context.dataStore: DataStore by preferencesDataStore(name = "hsu_connect_tokens")
+
+ private val accessTokenKey = stringPreferencesKey("access_token")
+ private val refreshTokenKey = stringPreferencesKey("refresh_token")
+
+ val accessToken: Flow = context.dataStore.data
+ .map { preferences ->
+ preferences[accessTokenKey]
+ }
+
+ val refreshToken: Flow = context.dataStore.data
+ .map { preferences ->
+ preferences[refreshTokenKey]
+ }
+
+ suspend fun saveTokens(accessToken: String, refreshToken: String) {
+ context.dataStore.edit {
+ it[accessTokenKey] = accessToken
+ it[refreshTokenKey] = refreshToken
+ }
+ }
+
+ suspend fun clearTokens() {
+ context.dataStore.edit {
+ it.clear()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/paging/CommunityPagingSource.kt b/app/src/main/java/com/hsLink/hslink/data/paging/CommunityPagingSource.kt
new file mode 100644
index 0000000..1fe690b
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/paging/CommunityPagingSource.kt
@@ -0,0 +1,36 @@
+package com.hsLink.hslink.data.paging
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import com.hsLink.hslink.data.service.commuunity.CommunityPostService
+import com.hsLink.hslink.domain.model.community.CommunityPost
+import com.hsLink.hslink.domain.model.community.toEntity
+
+class CommunityPagingSource(
+ private val communityPostService: CommunityPostService,
+ private val type: String
+) : PagingSource() {
+
+ override suspend fun load(params: LoadParams): LoadResult {
+ val page = params.key ?: 0
+
+ return try {
+ val response = communityPostService.getCommunity(type = type, page = page)
+ val posts = response.result.posts.map { it.toEntity() }
+
+ LoadResult.Page(
+ data = posts,
+ prevKey = if (page == 0) null else page - 1,
+ nextKey = if (response.result.hasNext) page + 1 else null
+ )
+ } catch (e: Exception) {
+ LoadResult.Error(e)
+ }
+ }
+
+ override fun getRefreshKey(state: PagingState): Int? {
+ return state.anchorPosition?.let {
+ state.closestPageToPosition(it)?.prevKey?.plus(1) ?: state.closestPageToPosition(it)?.nextKey?.minus(1)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/AuthInterceptor.kt b/app/src/main/java/com/hsLink/hslink/data/remote/AuthInterceptor.kt
new file mode 100644
index 0000000..a8d3380
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/AuthInterceptor.kt
@@ -0,0 +1,27 @@
+package com.hsLink.hslink.data.remote
+
+import com.hsLink.hslink.data.local.TokenDataStore
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
+import okhttp3.Interceptor
+import okhttp3.Response
+import javax.inject.Inject
+
+class AuthInterceptor @Inject constructor(
+ private val tokenDataStore: TokenDataStore
+) : Interceptor {
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val accessToken = runBlocking {
+ tokenDataStore.accessToken.first()
+ }
+
+ val requestBuilder = chain.request().newBuilder()
+
+ if (!accessToken.isNullOrBlank()) {
+ requestBuilder.addHeader("Authorization", "Bearer $accessToken")
+ }
+
+ return chain.proceed(requestBuilder.build())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/datasource/CommunityPostDataSource.kt b/app/src/main/java/com/hsLink/hslink/data/remote/datasource/CommunityPostDataSource.kt
index 6784ca2..e403e1c 100644
--- a/app/src/main/java/com/hsLink/hslink/data/remote/datasource/CommunityPostDataSource.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/datasource/CommunityPostDataSource.kt
@@ -1,8 +1,8 @@
package com.hsLink.hslink.data.remote.datasource
import com.hsLink.hslink.core.network.BaseResponse
-import com.hsLink.hslink.data.dto.request.PostRequestDto
-import com.hsLink.hslink.data.dto.response.CommunityPostResponseDto
+import com.hsLink.hslink.data.dto.request.community.PostRequestDto
+import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
interface CommunityPostDataSource {
suspend fun createCommunityPost(
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/datasource/PostDataSource.kt b/app/src/main/java/com/hsLink/hslink/data/remote/datasource/PostDataSource.kt
index 02d2dbd..923e81b 100644
--- a/app/src/main/java/com/hsLink/hslink/data/remote/datasource/PostDataSource.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/datasource/PostDataSource.kt
@@ -1,8 +1,8 @@
package com.hsLink.hslink.data.remote.datasource
import com.hsLink.hslink.core.network.BaseResponse
-import com.hsLink.hslink.data.dto.response.PostPromotionDto
-import com.hsLink.hslink.data.dto.response.PostResponseDto
+import com.hsLink.hslink.data.dto.response.home.PostPromotionDto
+import com.hsLink.hslink.data.dto.response.home.PostResponseDto
interface PostDataSource {
suspend fun getPopularPost() : BaseResponse
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt b/app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt
index 9737b0d..9861bb5 100644
--- a/app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt
@@ -1,7 +1,7 @@
package com.hsLink.hslink.data.remote.datasource
-import com.hsLink.hslink.data.dto.response.MentorListResponseDto
-import com.hsLink.hslink.data.dto.response.UserProfileResponseDto
+import com.hsLink.hslink.data.dto.response.search.MentorListResponseDto
+import com.hsLink.hslink.data.dto.response.mypage.UserProfileResponseDto
interface SearchDataSource {
suspend fun getMentors(page: Int, size: Int): MentorListResponseDto
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/CommunityPostDataSourceImpl.kt b/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/CommunityPostDataSourceImpl.kt
index cb2ec8f..b54c10b 100644
--- a/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/CommunityPostDataSourceImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/CommunityPostDataSourceImpl.kt
@@ -1,8 +1,8 @@
package com.hsLink.hslink.data.remote.datasourceimpl
import com.hsLink.hslink.core.network.BaseResponse
-import com.hsLink.hslink.data.dto.request.PostRequestDto
-import com.hsLink.hslink.data.dto.response.CommunityPostResponseDto
+import com.hsLink.hslink.data.dto.request.community.PostRequestDto
+import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
import com.hsLink.hslink.data.remote.datasource.CommunityPostDataSource
import com.hsLink.hslink.data.service.commuunity.CommunityPostService
import javax.inject.Inject
@@ -13,7 +13,6 @@ class CommunityPostDataSourceImpl @Inject constructor(
override suspend fun createCommunityPost(
request : PostRequestDto
) : BaseResponse {
- // TODO: Replace dummy token with real access token
- return communityPostService.postCommunity(token = "", requestBody = request)
+ return communityPostService.postCommunity(requestBody = request)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/PostDataSourceImpl.kt b/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/PostDataSourceImpl.kt
index 45835c4..03b8699 100644
--- a/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/PostDataSourceImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/PostDataSourceImpl.kt
@@ -1,8 +1,8 @@
package com.hsLink.hslink.data.remote.datasourceimpl
import com.hsLink.hslink.core.network.BaseResponse
-import com.hsLink.hslink.data.dto.response.PostPromotionDto
-import com.hsLink.hslink.data.dto.response.PostResponseDto
+import com.hsLink.hslink.data.dto.response.home.PostPromotionDto
+import com.hsLink.hslink.data.dto.response.home.PostResponseDto
import com.hsLink.hslink.data.remote.datasource.PostDataSource
import com.hsLink.hslink.data.service.home.PostService
import javax.inject.Inject
diff --git a/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/SearchDataSourceImpl.kt b/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/SearchDataSourceImpl.kt
index e105d13..fcfbe58 100644
--- a/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/SearchDataSourceImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/SearchDataSourceImpl.kt
@@ -1,7 +1,7 @@
package com.hsLink.hslink.data.remote.datasourceimpl
-import com.hsLink.hslink.data.dto.response.MentorListResponseDto
-import com.hsLink.hslink.data.dto.response.UserProfileResponseDto
+import com.hsLink.hslink.data.dto.response.search.MentorListResponseDto
+import com.hsLink.hslink.data.dto.response.mypage.UserProfileResponseDto
import com.hsLink.hslink.data.remote.datasource.SearchDataSource
import com.hsLink.hslink.data.service.search.SearchService
import javax.inject.Inject
diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
index 3a499ec..308347e 100644
--- a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
@@ -1,13 +1,15 @@
package com.hsLink.hslink.data.repositoryimpl
-import com.hsLink.hslink.data.dto.request.SocialLoginRequestDto
-import com.hsLink.hslink.data.dto.response.SocialLoginResponseDto
+import com.hsLink.hslink.data.dto.request.auth.SocialLoginRequestDto
+import com.hsLink.hslink.data.dto.response.auth.SocialLoginResponseDto
+import com.hsLink.hslink.data.local.TokenDataStore
import com.hsLink.hslink.data.service.login.AuthService
import com.hsLink.hslink.domain.repository.AuthRepository
import javax.inject.Inject
class AuthRepositoryImpl @Inject constructor(
- private val authService: AuthService
+ private val authService: AuthService,
+ private val tokenDataStore: TokenDataStore
) : AuthRepository {
override suspend fun loginWithSocialToken(
@@ -20,6 +22,7 @@ class AuthRepositoryImpl @Inject constructor(
if (response.isSuccessful) {
response.body()?.let { loginResponse ->
+ tokenDataStore.saveTokens(loginResponse.accessToken, loginResponse.refreshToken)
Result.success(loginResponse)
} ?: Result.failure(Exception("응답이 비어있습니다"))
} else {
diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt
index f1d16d2..e6a7f1e 100644
--- a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt
@@ -1,20 +1,34 @@
package com.hsLink.hslink.data.repositoryimpl
-import com.hsLink.hslink.data.dto.request.PostRequestDto
-import com.hsLink.hslink.data.dto.response.CommunityPostResponseDto
-import com.hsLink.hslink.data.remote.datasourceimpl.CommunityPostDataSourceImpl
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import com.hsLink.hslink.data.dto.request.community.PostRequestDto
+import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
+import com.hsLink.hslink.data.paging.CommunityPagingSource
+import com.hsLink.hslink.data.service.commuunity.CommunityPostService
+import com.hsLink.hslink.domain.model.community.CommunityPost
import com.hsLink.hslink.domain.repository.community.CommunityRepository
+import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class CommunityRepositoryImpl @Inject constructor(
- private val communityPostDataSourceImpl: CommunityPostDataSourceImpl,
+ private val communityPostService: CommunityPostService,
) : CommunityRepository {
- override suspend fun createCommunityPost(communityRequestDto: PostRequestDto): Result = runCatching {
- val response = communityPostDataSourceImpl.createCommunityPost(communityRequestDto)
- if (response.isSuccess) {
- response.result
- } else {
- throw Exception(response.message)
+ override suspend fun createCommunityPost(communityRequestDto: PostRequestDto): Result =
+ runCatching {
+ val response = communityPostService.postCommunity(communityRequestDto)
+ if (response.isSuccess) {
+ response.result
+ } else {
+ throw Exception(response.message)
+ }
}
+
+ override fun getCommunityPosts(type: String): Flow> {
+ return Pager(
+ config = PagingConfig(pageSize = 20),
+ pagingSourceFactory = { CommunityPagingSource(communityPostService, type) }
+ ).flow
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/home/PostRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/home/PostRepositoryImpl.kt
index 41ad244..12e2ed1 100644
--- a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/home/PostRepositoryImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/home/PostRepositoryImpl.kt
@@ -1,6 +1,6 @@
package com.hsLink.hslink.data.repositoryimpl.home
-import com.hsLink.hslink.data.dto.response.toEntity
+import com.hsLink.hslink.data.dto.response.home.toEntity
import com.hsLink.hslink.data.remote.datasourceimpl.PostDataSourceImpl
import com.hsLink.hslink.domain.model.home.PostPopularEntity
import com.hsLink.hslink.domain.model.home.PostPromotionEntity
diff --git a/app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt b/app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt
index 7868e67..2068cfb 100644
--- a/app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt
@@ -1,17 +1,23 @@
package com.hsLink.hslink.data.service.commuunity
import com.hsLink.hslink.core.network.BaseResponse
-import com.hsLink.hslink.data.dto.request.PostRequestDto
-import com.hsLink.hslink.data.dto.response.CommunityPostResponseDto
+import com.hsLink.hslink.data.dto.request.community.PostRequestDto
+import com.hsLink.hslink.data.dto.response.community.CommunityListResponseDto
+import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
import retrofit2.http.Body
-import retrofit2.http.Header
+import retrofit2.http.GET
import retrofit2.http.POST
-
+import retrofit2.http.Query
interface CommunityPostService {
@POST("posts")
suspend fun postCommunity(
- @Header("Authorization") token: String,
@Body requestBody: PostRequestDto,
): BaseResponse
+
+ @GET("posts")
+ suspend fun getCommunity(
+ @Query("type") type: String,
+ @Query("page") page: Int,
+ ): BaseResponse
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/service/home/PostService.kt b/app/src/main/java/com/hsLink/hslink/data/service/home/PostService.kt
index 86681f0..5a2c369 100644
--- a/app/src/main/java/com/hsLink/hslink/data/service/home/PostService.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/service/home/PostService.kt
@@ -1,8 +1,8 @@
package com.hsLink.hslink.data.service.home
import com.hsLink.hslink.core.network.BaseResponse
-import com.hsLink.hslink.data.dto.response.PostPromotionDto
-import com.hsLink.hslink.data.dto.response.PostResponseDto
+import com.hsLink.hslink.data.dto.response.home.PostPromotionDto
+import com.hsLink.hslink.data.dto.response.home.PostResponseDto
import retrofit2.http.GET
diff --git a/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt b/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
index 6b63311..84be672 100644
--- a/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
@@ -1,7 +1,7 @@
package com.hsLink.hslink.data.service.login
-import com.hsLink.hslink.data.dto.request.SocialLoginRequestDto
-import com.hsLink.hslink.data.dto.response.SocialLoginResponseDto
+import com.hsLink.hslink.data.dto.request.auth.SocialLoginRequestDto
+import com.hsLink.hslink.data.dto.response.auth.SocialLoginResponseDto
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.POST
diff --git a/app/src/main/java/com/hsLink/hslink/data/service/search/SearchService.kt b/app/src/main/java/com/hsLink/hslink/data/service/search/SearchService.kt
index 39e83df..7191340 100644
--- a/app/src/main/java/com/hsLink/hslink/data/service/search/SearchService.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/service/search/SearchService.kt
@@ -1,7 +1,7 @@
package com.hsLink.hslink.data.service.search
-import com.hsLink.hslink.data.dto.response.MentorListResponseDto
-import com.hsLink.hslink.data.dto.response.UserProfileResponseDto
+import com.hsLink.hslink.data.dto.response.search.MentorListResponseDto
+import com.hsLink.hslink.data.dto.response.mypage.UserProfileResponseDto
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
diff --git a/app/src/main/java/com/hsLink/hslink/domain/model/community/CommunityPost.kt b/app/src/main/java/com/hsLink/hslink/domain/model/community/CommunityPost.kt
new file mode 100644
index 0000000..f0f9f93
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/domain/model/community/CommunityPost.kt
@@ -0,0 +1,23 @@
+package com.hsLink.hslink.domain.model.community
+
+import com.hsLink.hslink.data.dto.response.community.CommunityListDto
+
+data class CommunityPost(
+ val id: Int,
+ val title: String,
+ val summary: String,
+ val author: String,
+ val studentId: String,
+ val authorStatus: String,
+)
+
+fun CommunityListDto.toEntity(): CommunityPost {
+ return CommunityPost(
+ id = id,
+ title = title,
+ summary = summary,
+ author = author,
+ studentId = studentId,
+ authorStatus = authorStatus,
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
index acc0dab..b32597a 100644
--- a/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
+++ b/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
@@ -1,6 +1,6 @@
package com.hsLink.hslink.domain.repository
-import com.hsLink.hslink.data.dto.response.SocialLoginResponseDto
+import com.hsLink.hslink.data.dto.response.auth.SocialLoginResponseDto
interface AuthRepository {
suspend fun loginWithSocialToken(
diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt
index 0a08789..48addc9 100644
--- a/app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt
+++ b/app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt
@@ -1,8 +1,13 @@
package com.hsLink.hslink.domain.repository.community
-import com.hsLink.hslink.data.dto.request.PostRequestDto
-import com.hsLink.hslink.data.dto.response.CommunityPostResponseDto
+import androidx.paging.PagingData
+import com.hsLink.hslink.data.dto.request.community.PostRequestDto
+import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
+import com.hsLink.hslink.domain.model.community.CommunityPost
+import kotlinx.coroutines.flow.Flow
interface CommunityRepository {
suspend fun createCommunityPost(communityRequestDto: PostRequestDto): Result
+
+ fun getCommunityPosts(type: String): Flow>
}
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/community/component/main/CommunityCardItem.kt b/app/src/main/java/com/hsLink/hslink/presentation/community/component/main/CommunityCardItem.kt
index 49693d1..33a3133 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/community/component/main/CommunityCardItem.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/community/component/main/CommunityCardItem.kt
@@ -30,6 +30,8 @@ private fun PreviewCommunityCardItem() {
userName = "John Doe",
userMajor = "Computer Science",
userInfo = "Senior at XYZ University",
+ userId = "12345",
+ author = "Author Name",
onClick = {},
)
}
@@ -40,6 +42,8 @@ fun CommunityCardItem(
userName: String,
userMajor: String,
userInfo: String,
+ userId:String,
+ author:String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
@@ -75,13 +79,52 @@ fun CommunityCardItem(
overflow = TextOverflow.Ellipsis
)
- Text(
- text = userInfo,
- color = HsLinkTheme.colors.Grey400,
- style = HsLinkTheme.typography.caption_12Normal,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis
- )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(2.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ){
+ Text(
+ text = author,
+ color = HsLinkTheme.colors.Grey400,
+ style = HsLinkTheme.typography.caption_12Normal,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ Text(
+ text = "·",
+ color = HsLinkTheme.colors.Grey700,
+ style = HsLinkTheme.typography.title_16Strong,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ Text(
+ text = userId,
+ color = HsLinkTheme.colors.Grey400,
+ style = HsLinkTheme.typography.caption_12Normal,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ Text(
+ text = "·",
+ color = HsLinkTheme.colors.Grey700,
+ style = HsLinkTheme.typography.title_16Strong,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ Text(
+ text = userInfo,
+ color = HsLinkTheme.colors.Grey400,
+ style = HsLinkTheme.typography.caption_12Normal,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/community/navigation/main/Communitynavigation.kt b/app/src/main/java/com/hsLink/hslink/presentation/community/navigation/main/Communitynavigation.kt
index bdcbe0f..7888c84 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/community/navigation/main/Communitynavigation.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/community/navigation/main/Communitynavigation.kt
@@ -15,16 +15,14 @@ fun NavController.navigateToCommunity(navOptions: NavOptions? = null) {
fun NavGraphBuilder.communityNavGraph(
padding: PaddingValues,
- navigateUp : () -> Unit,
- navigateToWriting : () -> Unit,
- navigateToPost: (String) -> Unit,
+ navigateToWriteCommunity : () -> Unit,
+ navigateToPost: (Int) -> Unit,
) {
composable {
CommunityRoute(
- padding,
- navigateUp = navigateUp,
- navigateWriteCommunity = navigateToWriting,
- navigateToPost = navigateToPost
+ paddingValues = padding,
+ navigateWriteCommunity = navigateToWriteCommunity,
+ navigateToPost = navigateToPost,
)
}
}
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/community/screen/main/CommunityScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/community/screen/main/CommunityScreen.kt
index aec5f31..895fcf1 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/community/screen/main/CommunityScreen.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/community/screen/main/CommunityScreen.kt
@@ -11,19 +11,18 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
+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.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
@@ -32,93 +31,60 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
+
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.paging.LoadState
+import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
import com.hsLink.hslink.R
import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar
import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme
import com.hsLink.hslink.core.util.noRippleClickable
+import com.hsLink.hslink.domain.model.community.CommunityPost
import com.hsLink.hslink.presentation.community.component.CommunityTab
import com.hsLink.hslink.presentation.community.component.CommunityTabLayout
+import com.hsLink.hslink.presentation.community.viewmodel.CommunityViewModel
import com.hsLink.hslink.presentation.home.component.CommunityCardItem
-data class CommunityPost(
- val id: String,
- val userName: String,
- val userMajor: String,
- val userInfo: String,
-)
-
@Preview(showBackground = true)
@Composable
private fun PreviewCommunityScreen() {
HsLinkTheme {
- CommunityScreen(
- paddingValues = PaddingValues(),
- navigateUp = {},
- navigateWriteCommunity = {},
- onClick = {},
- navigateToPost = {}
- )
}
}
@Composable
fun CommunityRoute(
paddingValues: PaddingValues,
- navigateUp: () -> Unit,
navigateWriteCommunity: () -> Unit,
- navigateToPost: (String) -> Unit,
+ navigateToPost: (Int) -> Unit,
+ viewModel: CommunityViewModel = hiltViewModel(),
) {
+ val selectedTab by viewModel.selectedTab.collectAsState(initial = CommunityTab.Popular)
+ val communityPosts = viewModel.communityPosts.collectAsLazyPagingItems()
+
CommunityScreen(
paddingValues = paddingValues,
- navigateUp = navigateUp,
navigateWriteCommunity = navigateWriteCommunity,
- onClick = {},
- navigateToPost = navigateToPost
+ navigateToPost = navigateToPost,
+ selectedTab = selectedTab,
+ onTabSelected = viewModel::selectTab,
+ posts = communityPosts
)
}
@Composable
fun CommunityScreen(
paddingValues: PaddingValues,
- navigateUp: () -> Unit,
navigateWriteCommunity: () -> Unit,
- onClick: () -> Unit,
- navigateToPost: (String) -> Unit,
+ navigateToPost: (Int) -> Unit,
modifier: Modifier = Modifier,
-
- ) {
- var selectedTab by remember { mutableStateOf(CommunityTab.Popular) }
-
- val posts = remember(selectedTab) {
- when (selectedTab) {
- CommunityTab.Popular -> listOf(
- CommunityPost("1", "인기글 작성자1", "컴퓨터공학과", "인기글 내용입니다"),
- CommunityPost("2", "인기글 작성자2", "경영학과", "좋아요가 많은 글"),
- CommunityPost("3", "인기글 작성자3", "디자인학과", "핫한 글입니다"),
- )
-
- CommunityTab.Free -> listOf(
- CommunityPost("4", "자유 작성자1", "전자공학과", "자유게시판 글1"),
- CommunityPost("5", "자유 작성자2", "수학과", "자유게시판 글2"),
- CommunityPost("6", "자유 작성자3", "물리학과", "자유게시판 글3"),
- )
-
- CommunityTab.Promotion -> listOf(
- CommunityPost("7", "홍보 작성자1", "마케팅학과", "동아리 홍보합니다"),
- CommunityPost("8", "홍보 작성자2", "광고홍보학과", "행사 알림"),
- CommunityPost("9", "홍보 작성자3", "경제학과", "스터디 모집"),
- )
-
- CommunityTab.Notice -> listOf(
- CommunityPost("10", "관리자1", "학생처", "중요 공지사항"),
- CommunityPost("11", "관리자2", "교무처", "학사 일정 안내"),
- CommunityPost("12", "관리자3", "총학생회", "필독 공지"),
- )
- }
- }
-
+ selectedTab: CommunityTab,
+ onTabSelected: (CommunityTab) -> Unit,
+ posts: LazyPagingItems,
+) {
Column(
modifier = modifier
.fillMaxSize()
@@ -145,34 +111,79 @@ fun CommunityScreen(
CommunityTabLayout(
selectedTab = selectedTab,
- onTabSelected = { tab ->
- selectedTab = tab
- }
+ onTabSelected = onTabSelected
)
Box(
modifier = Modifier.fillMaxHeight()
) {
- LazyColumn(
- modifier = Modifier.fillMaxSize(),
- contentPadding = PaddingValues(
- start = 16.dp,
- end = 16.dp,
- top = 16.dp,
- bottom = 80.dp
- ),
- verticalArrangement = Arrangement.spacedBy(12.dp)
- ) {
- items(
- items = posts,
- key = { it.id }
- ) { post ->
- CommunityCardItem(
- userName = post.userName,
- userMajor = post.userMajor,
- userInfo = post.userInfo,
- onClick = { navigateToPost(post.id) }
- )
+ when (posts.loadState.refresh) {
+ is LoadState.Loading -> {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ CircularProgressIndicator()
+ }
+ }
+
+ is LoadState.Error -> {
+ val error = posts.loadState.refresh as LoadState.Error
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Text(
+ text = "데이터 로드 오류 발생: ${error.error.localizedMessage}",
+ color = Color.Red
+ )
+ }
+ }
+
+ else -> {
+ if (posts.itemCount == 0) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(text = "게시글이 존재하지 않습니다.", color = HsLinkTheme.colors.Grey500)
+ }
+ } else {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = PaddingValues(
+ start = 16.dp,
+ end = 16.dp,
+ top = 16.dp,
+ bottom = 80.dp
+ ),
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ items(
+ count = posts.itemCount,
+ key = { index -> posts[index]?.id ?: index }
+ ) { index ->
+ val post = posts[index]
+ post?.let {
+ CommunityCardItem(
+ userName = it.title,
+ userMajor = it.summary,
+ userId = it.studentId,
+ userInfo = it.authorStatus,
+ author = it.author,
+ onClick = { navigateToPost(it.id) }
+ )
+ }
+ }
+
+ if (posts.loadState.append is LoadState.Loading) {
+ item {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 8.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/community/screen/write/CommunityWritingScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/community/screen/write/CommunityWritingScreen.kt
index 32e7275..4f883ab 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/community/screen/write/CommunityWritingScreen.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/community/screen/write/CommunityWritingScreen.kt
@@ -24,6 +24,7 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
import com.hsLink.hslink.R
import com.hsLink.hslink.core.designsystem.component.HsLinkDialog
import com.hsLink.hslink.core.designsystem.component.HsLinkTextField
@@ -32,6 +33,7 @@ import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme
import com.hsLink.hslink.presentation.community.component.BoardSelectionField
import com.hsLink.hslink.presentation.community.component.BoardType
import com.hsLink.hslink.presentation.community.component.CommunityWriteButton
+import com.hsLink.hslink.presentation.community.viewmodel.CommunityViewModel
@Preview(showBackground = true)
@Composable
@@ -40,7 +42,8 @@ private fun CommunityWritingScreenPreview() {
CommunityWritingScreen(
paddingValues = PaddingValues(),
navigateUp = {},
- navigateToCommunity = {}
+ navigateToCommunity = {},
+ createPost = { _, _, _ -> }
)
}
}
@@ -50,11 +53,13 @@ fun CommunityWritingRoute(
paddingValues: PaddingValues,
navigateUp: () -> Unit,
navigateToCommunity: () -> Unit,
+ viewModel: CommunityViewModel = hiltViewModel()
) {
CommunityWritingScreen(
paddingValues = paddingValues,
navigateUp = navigateUp,
- navigateToCommunity = navigateToCommunity
+ navigateToCommunity = navigateToCommunity,
+ createPost = viewModel::createPost
)
}
@@ -63,6 +68,7 @@ fun CommunityWritingScreen(
paddingValues: PaddingValues,
navigateUp: () -> Unit,
navigateToCommunity: () -> Unit,
+ createPost: (String, String, String) -> Unit,
modifier: Modifier = Modifier,
) {
var selectedBoardType by remember { mutableStateOf(null) }
@@ -261,6 +267,9 @@ fun CommunityWritingScreen(
confirmText = "업로드하기",
dismissText = "취소하기",
onConfirm = {
+ selectedBoardType?.let { boardType ->
+ createPost(boardType.name, title, content)
+ }
showUploadDialog = false
navigateToCommunity()
},
@@ -269,4 +278,4 @@ fun CommunityWritingScreen(
}
)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt
index efe1b57..b8a2d2f 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt
@@ -2,24 +2,37 @@ package com.hsLink.hslink.presentation.community.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.hsLink.hslink.data.dto.request.PostRequestDto
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import com.hsLink.hslink.data.dto.request.community.PostRequestDto
+import com.hsLink.hslink.domain.model.community.CommunityPost
import com.hsLink.hslink.domain.repository.community.CommunityRepository
-import com.hsLink.hslink.presentation.community.state.CommunityContract
+import com.hsLink.hslink.presentation.community.component.CommunityTab
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class CommunityViewModel @Inject constructor(
private val repository: CommunityRepository
-) : ViewModel(){
+) : ViewModel() {
+
+ private val _selectedTab = MutableStateFlow(CommunityTab.Popular)
+ val selectedTab: Flow = _selectedTab
+
+ val communityPosts: Flow> = _selectedTab
+ .flatMapLatest { tab ->
+ repository.getCommunityPosts(tab.name.lowercase())
+ }
+ .cachedIn(viewModelScope)
+
+ fun selectTab(tab: CommunityTab) {
+ _selectedTab.value = tab
+ }
- private val _state = MutableStateFlow(CommunityContract())
- val state: StateFlow = _state.asStateFlow()
-
fun createPost(postType: String, title: String, body: String) {
viewModelScope.launch {
val request = PostRequestDto(
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt b/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
index 0acad41..39122cd 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
@@ -49,11 +49,11 @@ fun MainNavHost(
)
communityNavGraph(
- padding,
- navigateUp = navigator::navigateUp,
- navigateToWriting = navigator::navigateWriteCommunity,
- navigateToPost = navigator::navigateToCommunityPost
+ padding = padding,
+ navigateToWriteCommunity = navigator::navigateWriteCommunity,
+ navigateToPost = { postId -> navigator.navigateToCommunityPost(postId.toString()) },
)
+
mypageNavGraph(
padding = padding,
navController = navigator.navController
@@ -86,4 +86,4 @@ fun MainNavHost(
)
}
-}
\ No newline at end of file
+}