Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
9834a5c
Merge branch 'develop' into feature/#44
ugmin1030 Jan 28, 2026
01e35c7
📦 feature/file 모듈 의존성 변경: foundation-layout을 foundation으로 교체
ugmin1030 Jan 30, 2026
50a844e
✨ feature/file: 링크 삭제 모달 문구 수정 및 미사용 임포트 제거
ugmin1030 Feb 2, 2026
1326445
✨ feature/file: FileBottomSheet UI 스타일 수정 및 배경 딤(scrim) 효과 추가
ugmin1030 Feb 2, 2026
69c19e0
♻️ feature/file: TextFieldFileBottomSheet 화살표 회전 애니메이션 라벨 추가
ugmin1030 Feb 2, 2026
6d04654
✨ feature/file: LinkItemLayout UI 및 스타일 개선
ugmin1030 Feb 2, 2026
b27b3ed
✨ 링크 분류 바텀시트 UI 및 선택 로직 개선
ugmin1030 Feb 2, 2026
e9fbcb6
♻️ feature/file: 아이콘 컴포저블 접근 제한자 수정 (`internal` 추가)
ugmin1030 Feb 2, 2026
5af7142
🐛 검색 로직 수정: 초기화 시 불필요한 호출 방지
ugmin1030 Feb 3, 2026
12076b6
Merge branch 'refs/heads/develop' into feature/#44
ugmin1030 Feb 3, 2026
1b6b502
✨ EmailLoginScreen UI 구조 개선: 하단 메뉴 레이아웃 변경 (Box -> Row)
ugmin1030 Feb 3, 2026
0e93751
✨ feature/file: BottomFolderListMenu 리팩터링 및 스크롤 바 추가
ugmin1030 Feb 25, 2026
51de66c
✨ design: 원형 내부 그림자 효과를 위한 innerRingShadow Modifier 추가
ugmin1030 Feb 25, 2026
6320dbd
✨ `FileViewModel` 폴더 공개/비공개 전환 로직 분리 및 로그 추가
ugmin1030 Feb 25, 2026
99028c2
✨ feature/file : 폴더 아이템 레이아웃 UI 개선 및 기능 추가
ugmin1030 Feb 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions design/src/main/java/com/example/design/modifier/InnerRingShadow.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.example.design.modifier

import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
* 컴포넌트의 안쪽 테두리를 따라 부드러운 원형 그림자(Inner Ring Shadow) 효과를 적용합니다.
*
* 이 함수는 [Brush.radialGradient]를 사용하여 컴포넌트의 중심부터 가장자리까지 그레이디언트를 생성하며,
* 지정된 두께([edgeThickness]) 영역에만 [shadowColor]를 노출시켜 입체감을 부여합니다.
*
* @param shadowColor 가장자리 끝부분에 적용될 그림자의 색상 및 투명도입니다. 기본값은 10% 불투명도의 검정색입니다.
* @param edgeThickness 그림자가 그려질 가장자리의 두께입니다. 기본값은 2.dp입니다.
* @return 안쪽 그림자 효과가 적용된 [Modifier] 객체를 반환합니다.
*
* @see androidx.compose.ui.draw.drawWithCache
* @see androidx.compose.ui.graphics.Brush.Companion.radialGradient
*
* @sample
* Box(
* modifier = Modifier
* .size(100.dp)
* .clip(CircleShape)
* .innerRingShadow(shadowColor = Color.Black.copy(alpha = 0.2f), edgeThickness = 4.dp)
* )
*/
fun Modifier.innerRingShadow(
shadowColor: Color = Color.Black.copy(alpha = 0.10f),
edgeThickness: Dp = 2.dp
): Modifier = this.drawWithCache {

// ─────────────────────────────────────────────────────────────
// 1) 원형 반지름 계산
// ─────────────────────────────────────────────────────────────
// size: 이 Modifier가 적용되는 컴포넌트의 실제 픽셀 크기 (Size(widthPx, heightPx))
// minDimension: width/height 중 더 짧은 변 (원형 그림자 만들 때 "지름"으로 삼기 좋음)
//
// r: radialGradient의 반지름(px)
// - minDimension / 2: "짧은 변" 기준으로 원의 반지름을 잡음
// - coerceAtLeast(1f): 너무 작은 값(0에 가까운 값)으로 인해 0으로 나눔/이상 계산을 피함
val r = (size.minDimension / 2f).coerceAtLeast(1f)

// ─────────────────────────────────────────────────────────────
// 2) 테두리 쪽에만 그림자를 남기기 위한 "내부 링 두께"를 px로 변환
// ─────────────────────────────────────────────────────────────
// edgeThickness(Dp): 사용자 입력 두께(논리 단위) → toPx()로 "픽셀" 변환
//
// t: 테두리 내부 그림자가 차지할 두께(px)
// - coerceIn(0f, r): 두께가 음수가 되거나 반지름보다 커지면 계산이 깨질 수 있어서 범위 제한
// * 0f: 그림자 없음
// * r: 반지름 전체(즉, 거의 전체가 그림자)까지 허용
val t = edgeThickness.toPx().coerceIn(0f, r)

// ─────────────────────────────────────────────────────────────
// 3) Gradient에서 "투명 → 그림자"로 바뀌는 경계 비율 계산
// ─────────────────────────────────────────────────────────────
// radialGradient의 colorStops는 (0.0 ~ 1.0) 범위의 비율로 정의됨.
//
// (r - t) / r 의 의미:
// - r: 전체 반지름
// - r - t: "그림자가 시작되기 전"까지의 반지름(= 내부는 투명하게 두고 싶은 영역)
//
// 예) r=50px, t=5px 라면
// - (r - t) / r = 45/50 = 0.9
// - 즉, 중심~0.9 구간은 투명, 0.9~1.0 구간에서만 그림자로 변화
//
// coerceIn(0f, 1f): 비율이 범위를 벗어나면 gradient가 비정상 동작할 수 있어 제한
val stop = ((r - t) / r).coerceIn(0f, 1f)

// ─────────────────────────────────────────────────────────────
// 4) 중심은 투명, 가장자리로 갈수록 shadowColor가 되는 Radial Gradient 생성
// ─────────────────────────────────────────────────────────────
// Brush.radialGradient:
// - center: 그라디언트 중심점 (여기서는 컴포넌트 중앙)
// - radius: 반지름(px)
//
// colorStops:
// - 0.0f : 중심점(반지름 0) 위치
// - stop : "여기까지는 투명" 위치 (내부 영역)
// - 1.0f : 반지름 끝(가장자리) 위치
//
// 0.0f to Transparent + stop to Transparent:
// - 내부 영역(0~stop)을 완전히 투명하게 고정해 "테두리만" 그림자가 남게 함
//
// 1.0f to shadowColor:
// - 가장자리에서만 shadowColor가 적용되도록 하여,
// 두 번째 사진처럼 "희미한 내부 테두리 그림자" 느낌을 만들 수 있음
val brush = Brush.radialGradient(
colorStops = arrayOf(
0.0f to Color.Transparent, // 중심은 완전 투명
stop to Color.Transparent, // stop 지점까지도 완전 투명(= 내부 깨끗)
1.0f to shadowColor // 가장자리에만 그림자 색이 나타남
),
center = size.center, // 컴포넌트 중앙 기준으로 퍼짐
radius = r // 위에서 계산한 반지름(px)
)

// ─────────────────────────────────────────────────────────────
// 5) 실제 그리기: 기존 컨텐츠를 그리고, 그 위에 그라디언트를 덮어씌움
// ─────────────────────────────────────────────────────────────
onDrawWithContent {

// (1) 원래 컴포넌트 내용(배경, 텍스트 등) 먼저 그림
drawContent()

// (2) 위에서 만든 brush를 사각형 전체에 칠함
// - 원형으로만 보이게 하려면 반드시 바깥에서 clip(CircleShape) 같은 클립이 필요함!
// - clip이 없다면 "사각형 영역" 전체에 radialGradient가 적용됨.
//
// clip(CircleShape)가 이미 적용된 상태라면:
// - drawRect로 칠해도 원 밖은 잘려서 결국 "원 테두리 내부"에만 그림자가 남음
drawRect(brush = brush)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fun SearchBarTopSheet(
* - 350ms 디바운스
* - 동일 값 중복 호출 방지
*/
LaunchedEffect(text) {
LaunchedEffect(Unit) {
snapshotFlow { text }
.map { it.trim() }
.filter { it.length >= 2 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

검색했을 때, 한 글자만 검색한 사용자 입장에서 왜 검색이 안되지? 싶을 수 있어서, 디자이너와 상의 후 관련 ui 추가하는 건 어떠실까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

검색했을 때, 한 글자만 검색한 사용자 입장에서 왜 검색이 안되지? 싶을 수 있어서, 디자이너와 상의 후 관련 ui 추가하는 건 어떠실까요?

윤다인과 이다현의 TODO. 한글자일 때 비활성화+상태메시지 ui

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@ package com.example.curation.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -27,17 +24,14 @@ import com.example.curation.R
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
Expand All @@ -46,8 +40,6 @@ import com.example.curation.CurationViewModel
import com.example.curation.ui.list_card.LikedCurationCard
import com.example.curation.Paperlogy
import com.example.design.theme.LocalColorTheme
import com.example.design.theme.color.Basic
import com.example.design.R as Res
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.Locale
Expand Down
2 changes: 1 addition & 1 deletion feature/file/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.compose.foundation.layout)
implementation(libs.androidx.compose.foundation)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
64 changes: 49 additions & 15 deletions feature/file/src/main/java/com/example/file/FileViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,8 @@ class FileViewModel @Inject constructor(
onGetLinks = { list -> _notCategorizationLinks.value = list.map { it.copy() } }
)

Log.d("FileViewModel", "getNotCategorizationLinks try result: ${_subFolders.value}")

Log.d("FileViewModel", "getNotCategorizationLinks try result: ${_notCategorizationLinks.value}")

} catch (e: Exception) {
Expand Down Expand Up @@ -1055,24 +1057,25 @@ class FileViewModel @Inject constructor(
}
Log.d("FileViewModel", "receiveSharedFolder return")
}

// 공개/비공개 전환
fun changeSharing(folder: FolderSimpleInfo){
Log.d("FileViewModel", "changeSharing")
// 공개 전환
fun folderToShare(folder: FolderSimpleInfo){
Log.d("FileViewModel", "folderToShare")

viewModelScope.launch {
Log.d("FileViewModel", "changeSharing launch")
Log.d("FileViewModel", "folderToShare launch")

startLoading()
_errorMessage.value = null

try{
Log.d("FileViewModel", "changeSharing try")
try {
Log.d("FileViewModel", "folderToShare try")

val isSharing = folder.isSharing == "share"
val isPrivate = folder.isSharing == "private"

if (isPrivate) {
Log.d("FileViewModel", "folderToShare isPrivate true")

if(!isSharing){
folderRepository.setFolderViewerPermission(folder.folderId)
//folderRepository.setFolderPublicPermission(folder.folderId)

_subFolders.update { list ->
list.map {
Expand All @@ -1083,8 +1086,39 @@ class FileViewModel @Inject constructor(
}
}
}
}
Log.d("FileViewModel", "folderToShare try result")
} catch (e: Exception) {
Log.d("FileViewModel", "folderToShare catch: $e.message")

_errorMessage.value = e.message
} finally {
Log.d("FileViewModel", "folderToShare finally")

stopLoading()
}
}
Log.d("FileViewModel", "folderToShare return")

}
// 비공개 전환
fun folderToPrivate(folder: FolderSimpleInfo){
Log.d("FileViewModel", "folderToPrivate")

viewModelScope.launch {
Log.d("FileViewModel", "folderToPrivate launch")

startLoading()
_errorMessage.value = null

try{
Log.d("FileViewModel", "folderToPrivate try")

val isSharing = folder.isSharing == "share"

if(isSharing){
Log.d("FileViewModel", "folderToPrivate isSharing true")

}else{
folderRepository.setFolderPrivatePermission(folder.folderId)

_subFolders.update { list ->
Expand All @@ -1098,18 +1132,18 @@ class FileViewModel @Inject constructor(
}
}

Log.d("FileViewModel", "changeSharing try result")
Log.d("FileViewModel", "folderToPrivate try result")
}catch (e: Exception){
Log.d("FileViewModel", "changeSharing catch: $e.message")
Log.d("FileViewModel", "folderToPrivate catch: $e.message")

_errorMessage.value = e.message
}finally {
Log.d("FileViewModel", "changeSharing finally")
Log.d("FileViewModel", "folderToPrivate finally")

stopLoading()
}
}
Log.d("FileViewModel", "changeSharing return")
Log.d("FileViewModel", "folderToPrivate return")
}

// ---------- share method ----------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down Expand Up @@ -38,9 +37,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.file.R
import com.example.design.modifier.noRippleClickable
import com.example.design.theme.color.Basic
import com.example.file.R
import com.example.file.ui.theme.Black
import com.example.file.ui.theme.DefaultFont
import com.example.file.ui.theme.Gray300
Expand Down Expand Up @@ -73,6 +72,9 @@ fun FileBottomSheet(
color = Basic.gray[300]
)
},

// 딤 효과 수치
scrimColor = Basic.black.copy(alpha = 0.5f),
sheetState = sheetState,
onDismissRequest = onDismiss,
tonalElevation = 8.dp,
Expand All @@ -85,26 +87,29 @@ fun FileBottomSheet(
.padding(bottom = 20.dp)
.padding(horizontal = 20.dp),
) {

Text(
modifier = Modifier
.padding(start = 10.dp),
text = title,
fontSize = 18.sp,
lineHeight = 22.sp,
fontFamily = DefaultFont,
fontWeight = FontWeight(500),
color = Black,
)

Spacer(modifier = Modifier.height(11.dp))

Text(
modifier = Modifier
.padding(start = 10.dp, top = 14.dp),
.padding(start = 10.dp),
text = body,
fontSize = 15.sp,
lineHeight = 22.sp,
fontFamily = DefaultFont,
fontWeight = FontWeight.Normal,
color = Gray600,
)

Spacer(modifier = Modifier.height(24.dp))

content()
Expand Down
Loading