Skip to content

Commit a916f43

Browse files
committed
fix(git): 凭据编辑页提到 Scaffold 外独立渲染,消除双层顶栏
CredentialEditorScreen 自带 Scaffold/TopAppBar/BackHandler,原先嵌在 GitScreen 的 Scaffold padding Column 内覆盖渲染,导致外层'凭据与署名'与内层'添加/编辑凭据'两层 顶栏叠显;返回还靠 GitScreen 顶栏 navigationIcon 手写返回栈与其自身 BackHandler 双 重处理。 改为编辑/新增态在 Scaffold 之前提前 return 独立全屏渲染,仅留一层顶栏,返回统一由 CredentialEditorScreen 的 BackHandler 接管。顺带统一:编辑态保存/删除后回列表(原停 留编辑页)。行为为纯 UI 修复,冒烟 :app:assembleUniversalDebug 通过。
1 parent db01f09 commit a916f43

1 file changed

Lines changed: 55 additions & 111 deletions

File tree

  • app/src/main/java/com/aicode/feature/git/presentation/component

app/src/main/java/com/aicode/feature/git/presentation/component/GitScreen.kt

Lines changed: 55 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,32 @@ fun GitScreen(
101101
var showCommitDialog by remember { mutableStateOf(false) }
102102
var showCredentials by remember { mutableStateOf(false) }
103103
// editingCredential != null -> 编辑现有;editingCredential == null && isAddingCredential -> 新增;否则列表态。
104+
// 编辑/新增态直接在 [Scaffold] 之外独立渲染全屏 [CredentialEditorScreen](它自带 Scaffold/TopAppBar/BackHandler),
105+
// 避免与本页 Scaffold 嵌套产生双层顶栏,返回由其自身 BackHandler 接管。
104106
var editingCredential by remember { mutableStateOf<GitCredential?>(null) }
105107
var isAddingCredential by remember { mutableStateOf(false) }
106108

109+
// 编辑/新增凭据:独立全屏页,不进入下方 GitScreen 的 Scaffold,避免双层顶栏。
110+
if (editingCredential != null) {
111+
val editing = editingCredential!!
112+
CredentialEditorScreen(
113+
initial = editing,
114+
onBack = { editingCredential = null },
115+
onSave = { credentialViewModel.saveCredential(it); editingCredential = null },
116+
onDelete = { credentialViewModel.deleteCredential(it); editingCredential = null }
117+
)
118+
return
119+
}
120+
if (isAddingCredential) {
121+
CredentialEditorScreen(
122+
initial = null,
123+
onBack = { isAddingCredential = false },
124+
onSave = { credentialViewModel.saveCredential(it); isAddingCredential = false },
125+
onDelete = { /* 新增态无删除 */ }
126+
)
127+
return
128+
}
129+
107130
Scaffold(
108131
containerColor = MaterialTheme.colorScheme.background,
109132
topBar = {
@@ -115,13 +138,8 @@ fun GitScreen(
115138
),
116139
navigationIcon = {
117140
IconButton(onClick = {
118-
// 子页优先回上一级:编辑/新增 -> 凭据列表 -> 退出 Git 页
119-
when {
120-
editingCredential != null -> editingCredential = null
121-
isAddingCredential -> isAddingCredential = false
122-
showCredentials -> showCredentials = false
123-
else -> onNavigateBack()
124-
}
141+
// 凭据列表态回 Git 页,否则退出 Git 页。编辑/新增态由 [CredentialEditorScreen] 自身 BackHandler 处理,不走此顶栏。
142+
if (showCredentials) showCredentials = false else onNavigateBack()
125143
}) {
126144
Icon(FeatherIcons.ArrowLeft, contentDescription = "返回")
127145
}
@@ -134,7 +152,8 @@ fun GitScreen(
134152
IconButton(onClick = { viewModel.refresh() }, enabled = !state.busy) {
135153
Icon(FeatherIcons.RefreshCw, contentDescription = "刷新")
136154
}
137-
} else if (editingCredential == null && !isAddingCredential) {
155+
} else {
156+
// showCredentials 列表态:显示添加凭据。编辑/新增态已 return,渲染顶栏时不会落到此分支。
138157
IconButton(onClick = { isAddingCredential = true }) {
139158
Icon(FeatherIcons.Plus, contentDescription = "添加凭据")
140159
}
@@ -145,26 +164,6 @@ fun GitScreen(
145164
snackbarHost = { SnackbarHost(snackbarHostState) }
146165
) { padding ->
147166
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
148-
// 凭据编辑/新增子页(全屏覆盖 Git tab 内容)
149-
val editing = editingCredential
150-
if (editing != null) {
151-
CredentialEditorScreen(
152-
initial = editing,
153-
onBack = { editingCredential = null },
154-
onSave = { credentialViewModel.saveCredential(it) },
155-
onDelete = { credentialViewModel.deleteCredential(it) }
156-
)
157-
return@Column
158-
}
159-
if (isAddingCredential) {
160-
CredentialEditorScreen(
161-
initial = null,
162-
onBack = { isAddingCredential = false },
163-
onSave = { credentialViewModel.saveCredential(it); isAddingCredential = false },
164-
onDelete = { /* 新增态无删除 */ }
165-
)
166-
return@Column
167-
}
168167
if (showCredentials) {
169168
// 每次进入凭据页重新读署名:用户可能在终端改过项目级/全局署名,避免回显陈旧空值。
170169
LaunchedEffect(Unit) { credentialViewModel.refreshIdentity() }
@@ -201,7 +200,7 @@ fun GitScreen(
201200
state.loading -> Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
202201
CircularProgressIndicator()
203202
}
204-
state.notARepo -> EmptyState("当前工作区不是 Git 仓库")
203+
state.notARepo -> NotARepoState(onInit = viewModel::initRepo)
205204
else -> when (state.tab) {
206205
GitTab.STATUS -> StatusTab(
207206
status = state.status,
@@ -237,88 +236,6 @@ fun GitScreen(
237236
}
238237
)
239238
}
240-
241-
// push/pull 缺凭据时:VM 置位 pendingCredentialHost,此处弹登录框,填完保存落盘 + 自动重试原操作。
242-
state.pendingCredentialHost?.let { host ->
243-
CredentialPromptDialog(
244-
host = host,
245-
onConfirm = { username, token ->
246-
viewModel.saveCredentialAndRetry(host, username, token)
247-
},
248-
onDismiss = { viewModel.cancelPendingCredential() }
249-
)
250-
}
251-
}
252-
253-
/**
254-
* 拉取/推送缺 https 凭据时的登录弹窗。host 只读预填(来自当前 remote),用户填 username/token;
255-
* 填完确认后 VM 存 Room + 落盘到容器持久挂载 + 自动重试原操作。仿 Win git 无凭据弹窗登录体验。
256-
* 新存的凭据自动设为该 host 默认(与 findForHost 的 isDefault 优先语义一致)。
257-
*/
258-
@OptIn(ExperimentalMaterial3Api::class)
259-
@Composable
260-
private fun CredentialPromptDialog(
261-
host: String,
262-
onConfirm: (username: String, token: String) -> Unit,
263-
onDismiss: () -> Unit
264-
) {
265-
var username by remember { mutableStateOf("") }
266-
var token by remember { mutableStateOf("") }
267-
var tokenVisible by remember { mutableStateOf(false) }
268-
val canSave = username.trim().isNotBlank() && token.isNotBlank()
269-
270-
AlertDialog(
271-
onDismissRequest = onDismiss,
272-
title = { Text("需要 $host 的登录凭据") },
273-
text = {
274-
Column(verticalArrangement = Arrangement.spacedBy(Spacing.md)) {
275-
OutlinedTextField(
276-
value = host,
277-
onValueChange = { /* host 来自 remote,只读 */ },
278-
label = { Text("远程主机") },
279-
singleLine = true,
280-
readOnly = true,
281-
modifier = Modifier.fillMaxWidth()
282-
)
283-
OutlinedTextField(
284-
value = username,
285-
onValueChange = { username = it },
286-
label = { Text("用户名") },
287-
singleLine = true,
288-
modifier = Modifier.fillMaxWidth()
289-
)
290-
OutlinedTextField(
291-
value = token,
292-
onValueChange = { token = it },
293-
label = { Text("访问令牌 Token / PAT") },
294-
singleLine = true,
295-
visualTransformation = if (tokenVisible) VisualTransformation.None else PasswordVisualTransformation(),
296-
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
297-
trailingIcon = {
298-
IconButton(onClick = { tokenVisible = !tokenVisible }) {
299-
Icon(
300-
if (tokenVisible) FeatherIcons.EyeOff else FeatherIcons.Eye,
301-
contentDescription = if (tokenVisible) "隐藏" else "显示"
302-
)
303-
}
304-
},
305-
modifier = Modifier.fillMaxWidth()
306-
)
307-
Text(
308-
text = "填完即保存并自动重试。该凭据同时会保存到容器内 git,终端与 AI 执行裸 git 命令时也可直接使用。",
309-
style = MaterialTheme.typography.bodySmall,
310-
color = MaterialTheme.colorScheme.onSurfaceVariant
311-
)
312-
}
313-
},
314-
confirmButton = {
315-
TextButton(
316-
onClick = { if (canSave) onConfirm(username.trim(), token) },
317-
enabled = canSave
318-
) { Text("保存并重试") }
319-
},
320-
dismissButton = { TextButton(onClick = onDismiss) { Text("取消") } }
321-
)
322239
}
323240

324241
@Composable
@@ -1190,6 +1107,33 @@ private fun EmptyState(text: String) {
11901107
}
11911108
}
11921109

1110+
/** 非仓库态:文案 + 「初始化 Git 仓库」按钮(跑 `git init`,成功后自动刷新进仓库态)。 */
1111+
@Composable
1112+
private fun NotARepoState(onInit: () -> Unit) {
1113+
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
1114+
Column(
1115+
horizontalAlignment = Alignment.CenterHorizontally,
1116+
verticalArrangement = Arrangement.spacedBy(Spacing.md)
1117+
) {
1118+
Text(
1119+
"当前工作区不是 Git 仓库",
1120+
style = MaterialTheme.typography.bodyMedium,
1121+
color = MaterialTheme.colorScheme.onSurfaceVariant
1122+
)
1123+
Text(
1124+
"初始化后会在此创建 .git,之后可暂存、提交、关联远程。",
1125+
style = MaterialTheme.typography.bodySmall,
1126+
color = MaterialTheme.colorScheme.onSurfaceVariant
1127+
)
1128+
FilledTonalButton(onClick = onInit) {
1129+
Icon(FeatherIcons.GitBranch, contentDescription = null)
1130+
Spacer(Modifier.width(Spacing.sm))
1131+
Text("初始化 Git 仓库")
1132+
}
1133+
}
1134+
}
1135+
}
1136+
11931137
@Composable
11941138
private fun CommitDialog(onDismiss: () -> Unit, onConfirm: (String) -> Unit) {
11951139
var message by remember { mutableStateOf("") }

0 commit comments

Comments
 (0)