Skip to content

Commit 379a8be

Browse files
authored
Merge pull request #73 from YAPP-Github/feat/#59,64,69-archive-api
[feat] #59, #64, #69 아카이브 API 연동 및 페이징 구현
2 parents 33e0c3a + 75ed9d5 commit 379a8be

47 files changed

Lines changed: 1130 additions & 676 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/data-api/build.gradle.kts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
plugins {
2-
alias(libs.plugins.neki.kotlin.library)
2+
alias(libs.plugins.neki.android.library)
3+
}
4+
5+
android {
6+
namespace = "com.neki.android.core.dataapi"
37
}
48

59
dependencies {
610
implementation(projects.core.model)
711
implementation(libs.kotlinx.coroutines.core)
812
api(libs.androidx.datastore.preferences)
13+
api(libs.androidx.paging.runtime)
914
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.neki.android.core.dataapi.repository
2+
3+
import com.neki.android.core.model.AlbumPreview
4+
5+
interface FolderRepository {
6+
suspend fun getFolders(): Result<List<AlbumPreview>>
7+
suspend fun createFolder(name: String): Result<Unit>
8+
suspend fun deleteFolder(id: List<Long>, deletePhotos: Boolean): Result<Unit>
9+
suspend fun removePhotosFromFolder(folderId: Long, photoIds: List<Long>): Result<Unit>
10+
}

core/data-api/src/main/java/com/neki/android/core/dataapi/repository/MediaUploadRepository.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
package com.neki.android.core.dataapi.repository
22

3+
import android.net.Uri
34
import com.neki.android.core.model.ContentType
45
import com.neki.android.core.model.MediaUploadTicket
56

67
interface MediaUploadRepository {
7-
suspend fun getUploadTicket(
8-
uploadCount: Int = 1,
8+
suspend fun getSingleUploadTicket(
9+
fileName: String,
10+
contentType: String,
11+
mediaType: String,
12+
): Result<MediaUploadTicket>
13+
14+
suspend fun getMultipleUploadTicket(
15+
uploadCount: Int,
916
fileName: String,
1017
contentType: String,
1118
mediaType: String,
@@ -16,4 +23,16 @@ interface MediaUploadRepository {
1623
imageBytes: ByteArray,
1724
contentType: ContentType,
1825
): Result<Unit>
26+
27+
suspend fun uploadImageFromUri(
28+
uploadUrl: String,
29+
uri: Uri,
30+
contentType: ContentType,
31+
): Result<Unit>
32+
33+
suspend fun uploadImageFromUrl(
34+
uploadUrl: String,
35+
imageUrl: String,
36+
contentType: ContentType,
37+
): Result<Unit>
1938
}

core/data-api/src/main/java/com/neki/android/core/dataapi/repository/PhotoRepository.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.neki.android.core.dataapi.repository
22

3+
import androidx.paging.PagingData
34
import com.neki.android.core.model.AlbumPreview
45
import com.neki.android.core.model.Photo
56
import com.neki.android.core.model.SortOrder
7+
import kotlinx.coroutines.flow.Flow
68

79
interface PhotoRepository {
810
suspend fun getPhotos(
@@ -28,4 +30,13 @@ interface PhotoRepository {
2830
): Result<List<Photo>>
2931

3032
suspend fun getFavoriteSummary(): Result<AlbumPreview>
33+
34+
fun getPhotosFlow(
35+
folderId: Long? = null,
36+
sortOrder: SortOrder = SortOrder.DESC,
37+
): Flow<PagingData<Photo>>
38+
39+
fun getFavoritePhotosFlow(
40+
sortOrder: SortOrder = SortOrder.DESC,
41+
): Flow<PagingData<Photo>>
3142
}

core/data/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ dependencies {
4242

4343
implementation(libs.androidx.datastore.core)
4444
implementation(libs.androidx.datastore.preferences)
45+
implementation(libs.androidx.paging.runtime)
4546
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.neki.android.core.data.paging
2+
3+
import androidx.paging.PagingSource
4+
import androidx.paging.PagingState
5+
import com.neki.android.core.data.remote.api.PhotoService
6+
import com.neki.android.core.model.Photo
7+
import com.neki.android.core.model.SortOrder
8+
9+
class FavoritePhotoPagingSource(
10+
private val photoService: PhotoService,
11+
private val sortOrder: SortOrder,
12+
) : PagingSource<Int, Photo>() {
13+
14+
override fun getRefreshKey(state: PagingState<Int, Photo>): Int? {
15+
return state.anchorPosition?.let { anchorPosition ->
16+
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
17+
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
18+
}
19+
}
20+
21+
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Photo> {
22+
return try {
23+
val page = params.key ?: 0
24+
val response = photoService.getFavoritePhotos(
25+
page = page,
26+
size = params.loadSize,
27+
sortOrder = sortOrder.name,
28+
)
29+
val photos = response.data.toModels()
30+
val hasNext = response.data.hasNext
31+
32+
LoadResult.Page(
33+
data = photos,
34+
prevKey = if (page == 0) null else page - 1,
35+
nextKey = if (hasNext) page + 1 else null,
36+
)
37+
} catch (e: Exception) {
38+
LoadResult.Error(e)
39+
}
40+
}
41+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.neki.android.core.data.paging
2+
3+
import androidx.paging.PagingSource
4+
import androidx.paging.PagingState
5+
import com.neki.android.core.data.remote.api.PhotoService
6+
import com.neki.android.core.model.Photo
7+
8+
class PhotoPagingSource(
9+
private val photoService: PhotoService,
10+
private val folderId: Long?,
11+
private val sortOrder: String,
12+
) : PagingSource<Int, Photo>() {
13+
14+
override fun getRefreshKey(state: PagingState<Int, Photo>): Int? {
15+
return state.anchorPosition?.let { anchorPosition ->
16+
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
17+
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
18+
}
19+
}
20+
21+
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Photo> {
22+
return try {
23+
val page = params.key ?: 0
24+
val response = photoService.getPhotos(
25+
folderId = folderId,
26+
page = page,
27+
size = params.loadSize,
28+
sortOrder = sortOrder,
29+
)
30+
val photos = response.data.toModels()
31+
val hasNext = response.data.hasNext
32+
33+
LoadResult.Page(
34+
data = photos,
35+
prevKey = if (page == 0) null else page - 1,
36+
nextKey = if (hasNext) page + 1 else null,
37+
)
38+
} catch (e: Exception) {
39+
LoadResult.Error(e)
40+
}
41+
}
42+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.neki.android.core.data.remote.api
2+
3+
import com.neki.android.core.data.remote.model.request.CreateFolderRequest
4+
import com.neki.android.core.data.remote.model.request.DeleteFolderRequest
5+
import com.neki.android.core.data.remote.model.request.DeletePhotoRequest
6+
import com.neki.android.core.data.remote.model.response.BasicNullableResponse
7+
import com.neki.android.core.data.remote.model.response.BasicResponse
8+
import com.neki.android.core.data.remote.model.response.CreateFolderResponse
9+
import com.neki.android.core.data.remote.model.response.FolderResponse
10+
import io.ktor.client.HttpClient
11+
import io.ktor.client.call.body
12+
import io.ktor.client.request.delete
13+
import io.ktor.client.request.get
14+
import io.ktor.client.request.parameter
15+
import io.ktor.client.request.post
16+
import io.ktor.client.request.setBody
17+
import javax.inject.Inject
18+
19+
class FolderService @Inject constructor(
20+
private val client: HttpClient,
21+
) {
22+
// 폴더 목록 조회
23+
suspend fun getFolders(): BasicResponse<FolderResponse> {
24+
return client.get("/api/folders").body()
25+
}
26+
27+
// 폴더 생성
28+
suspend fun createFolder(requestBody: CreateFolderRequest): BasicNullableResponse<CreateFolderResponse> {
29+
return client.post("/api/folders") { setBody(requestBody) }.body()
30+
}
31+
32+
// 폴더 삭제
33+
suspend fun deleteFolder(requestBody: DeleteFolderRequest, deletePhotos: Boolean): BasicNullableResponse<Unit> {
34+
return client.delete("/api/folders") {
35+
setBody(requestBody)
36+
parameter("deletePhotos", deletePhotos)
37+
}.body()
38+
}
39+
40+
// 폴더에서 사진 제거 (사진 자체는 삭제되지 않음)
41+
suspend fun removePhotosFromFolder(folderId: Long, requestBody: DeletePhotoRequest): BasicNullableResponse<Unit> {
42+
return client.delete("/api/folders/$folderId/photos") {
43+
setBody(requestBody)
44+
}.body()
45+
}
46+
}

core/data/src/main/java/com/neki/android/core/data/remote/api/PhotoService.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ class PhotoService @Inject constructor(
2727
folderId: Long? = null,
2828
page: Int = 0,
2929
size: Int = 20,
30+
sortOrder: String = "DESC",
3031
): BasicResponse<PhotoResponse> {
3132
return client.get("/api/photos") {
3233
parameter("folderId", folderId)
3334
parameter("page", page)
3435
parameter("size", size)
36+
parameter("sortOrder", sortOrder)
3537
}.body()
3638
}
3739

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.neki.android.core.data.remote.model.request
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class CreateFolderRequest(
8+
@SerialName("name") val name: String,
9+
)

0 commit comments

Comments
 (0)