Skip to content

Commit 4e6fb44

Browse files
committed
feat(login): Implement a standalone FN Connect login window functionality
1 parent 28a0303 commit 4e6fb44

File tree

4 files changed

+113
-13
lines changed

4 files changed

+113
-13
lines changed

composeApp/src/commonMain/kotlin/com/jankinwu/fntv/client/data/network/impl/FnOfficialApiImpl.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ class FnOfficialApiImpl() : FnOfficialApi {
280280
logger.i { "GET request, url: ${AccountDataCache.getFnOfficialBaseUrl()}$url, authx: $authx, parameters: $parameters, cookie: ${AccountDataCache.cookieState}" }
281281
val response = fnOfficialClient.get("${AccountDataCache.getFnOfficialBaseUrl()}$url") {
282282
header("Authx", authx)
283-
// header(HttpHeaders.Cookie, AccountDataCache.cookieState)
284283
parameters?.forEach { (key, value) ->
285284
if (value != null) {
286285
parameter(key, value)

composeApp/src/commonMain/kotlin/com/jankinwu/fntv/client/ui/screen/FnConnectWebViewScreen.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,10 @@ fun FnConnectWebViewScreen(
211211

212212
val normalizedUsername = capturedUsername.trim()
213213
.ifBlank { autoLoginUsername?.trim().orEmpty() }
214-
.ifBlank { "Unknown" }
214+
if (normalizedUsername.isNotBlank()) {
215+
PreferencesManager.getInstance().addLoginUsernameHistory(normalizedUsername)
216+
}
215217
val shouldRemember = capturedRememberMe && capturedPassword.isNotBlank()
216-
PreferencesManager.getInstance().addLoginUsernameHistory(normalizedUsername)
217218
logger.i("Remember me: $capturedRememberMe")
218219
val history = LoginHistory(
219220
host = "",

composeApp/src/commonMain/kotlin/com/jankinwu/fntv/client/ui/screen/LoginScreen.kt

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,21 @@ val CardBackgroundColor = Color(0xFF1A1D26).copy(alpha = 1f)
112112
val PrimaryBlue = Color(0xFF3A7BFF)
113113
val HintColor = Color.Gray
114114

115+
data class FnConnectWindowRequest(
116+
val initialUrl: String,
117+
val fnId: String,
118+
val autoLoginUsername: String? = null,
119+
val autoLoginPassword: String? = null,
120+
val allowAutoLogin: Boolean = false
121+
)
122+
115123
@OptIn(ExperimentalHazeMaterialsApi::class, ExperimentalComposeUiApi::class)
116124
@Suppress("RememberReturnType")
117125
@Composable
118126
fun LoginScreen(
119127
navigator: ComponentNavigator,
120-
draggableArea: @Composable (content: @Composable () -> Unit) -> Unit = { it() }
128+
draggableArea: @Composable (content: @Composable () -> Unit) -> Unit = { it() },
129+
onOpenFnConnectWindow: ((FnConnectWindowRequest) -> Unit)? = null
121130
) {
122131
var username by remember { mutableStateOf("") }
123132
var password by remember { mutableStateOf("") }
@@ -490,12 +499,27 @@ fun LoginScreen(
490499
showHistorySidebar = false
491500
AccountDataCache.isFnConnect = true
492501
AccountDataCache.fnId = fnId
493-
// onSwitchToFnConnect(url, fnId)
494-
showFnConnectWebView = true
495-
fnConnectUrl = url
496-
isAutoLogin = false
497-
fnAutoUsername = ""
498-
fnAutoPassword = ""
502+
val openWindow = onOpenFnConnectWindow
503+
if (openWindow != null) {
504+
openWindow(
505+
FnConnectWindowRequest(
506+
initialUrl = url,
507+
fnId = fnId,
508+
autoLoginUsername = null,
509+
autoLoginPassword = null,
510+
allowAutoLogin = false
511+
)
512+
)
513+
isAutoLogin = false
514+
fnAutoUsername = ""
515+
fnAutoPassword = ""
516+
} else {
517+
showFnConnectWebView = true
518+
fnConnectUrl = url
519+
isAutoLogin = false
520+
fnAutoUsername = ""
521+
fnAutoPassword = ""
522+
}
499523
} else {
500524
toastManager.showToast("请输入 FN ID", ToastType.Info)
501525
}
@@ -557,7 +581,20 @@ fun LoginScreen(
557581
loginHistoryList = upsertLoginHistory(loginHistoryList, history.copy(password = null))
558582
preferencesManager.saveLoginHistory(loginHistoryList)
559583
}
560-
showFnConnectWebView = true
584+
val openWindow = onOpenFnConnectWindow
585+
if (openWindow != null) {
586+
openWindow(
587+
FnConnectWindowRequest(
588+
initialUrl = fnConnectUrl,
589+
fnId = fnId,
590+
autoLoginUsername = fnAutoUsername,
591+
autoLoginPassword = fnAutoPassword,
592+
allowAutoLogin = isAutoLogin
593+
)
594+
)
595+
} else {
596+
showFnConnectWebView = true
597+
}
561598
} else {
562599
isFnConnect = false
563600
host = history.host
@@ -599,7 +636,7 @@ private fun getTextFieldColors() = OutlinedTextFieldDefaults.colors(
599636
unfocusedTextColor = Colors.TextSecondaryColor
600637
)
601638

602-
private fun upsertLoginHistory(current: List<LoginHistory>, incoming: LoginHistory): List<LoginHistory> {
639+
internal fun upsertLoginHistory(current: List<LoginHistory>, incoming: LoginHistory): List<LoginHistory> {
603640
fun normalize(value: String): String = value.trim().lowercase()
604641

605642
fun isSameIdentity(a: LoginHistory, b: LoginHistory): Boolean {

composeApp/src/jvmMain/kotlin/com/jankinwu/fntv/client/main.kt

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import androidx.compose.runtime.DisposableEffect
77
import androidx.compose.runtime.LaunchedEffect
88
import androidx.compose.runtime.collectAsState
99
import androidx.compose.runtime.getValue
10+
import androidx.compose.runtime.mutableStateOf
1011
import androidx.compose.runtime.remember
12+
import androidx.compose.runtime.setValue
1113
import androidx.compose.runtime.snapshotFlow
1214
import androidx.compose.ui.Alignment
1315
import androidx.compose.ui.graphics.painter.Painter
@@ -34,9 +36,12 @@ import com.jankinwu.fntv.client.ui.providable.LocalWebViewInitialized
3436
import com.jankinwu.fntv.client.ui.providable.LocalWebViewRestartRequired
3537
import com.jankinwu.fntv.client.ui.providable.LocalWindowHandle
3638
import com.jankinwu.fntv.client.ui.providable.LocalWindowState
39+
import com.jankinwu.fntv.client.ui.screen.FnConnectWebViewScreen
40+
import com.jankinwu.fntv.client.ui.screen.FnConnectWindowRequest
3741
import com.jankinwu.fntv.client.ui.screen.LoginScreen
3842
import com.jankinwu.fntv.client.ui.screen.PlayerManager
3943
import com.jankinwu.fntv.client.ui.screen.PlayerOverlay
44+
import com.jankinwu.fntv.client.ui.screen.upsertLoginHistory
4045
import com.jankinwu.fntv.client.utils.ConsoleLogWriter
4146
import com.jankinwu.fntv.client.utils.DesktopContext
4247
import com.jankinwu.fntv.client.utils.ExecutableDirectoryDetector
@@ -102,6 +107,7 @@ fun main() {
102107
KoinApplication(application = {
103108
modules(viewModelModule, apiModule)
104109
}) {
110+
var fnConnectWindowRequest by remember { mutableStateOf<FnConnectWindowRequest?>(null) }
105111
Window(
106112
onCloseRequest = ::exitApplication,
107113
state = state,
@@ -209,7 +215,10 @@ fun main() {
209215
if (!isLoggedIn) {
210216
LoginScreen(
211217
navigator = navigator,
212-
draggableArea = { content -> WindowDraggableArea(content = content) }
218+
draggableArea = { content -> WindowDraggableArea(content = content) },
219+
onOpenFnConnectWindow = { request ->
220+
fnConnectWindowRequest = request
221+
}
213222
)
214223
} else {
215224
App(
@@ -234,6 +243,60 @@ fun main() {
234243
}
235244
}
236245
}
246+
247+
val request = fnConnectWindowRequest
248+
if (request != null) {
249+
val fnConnectWindowState = rememberWindowState(
250+
size = DpSize(980.dp, 720.dp),
251+
position = WindowPosition.Aligned(Alignment.Center)
252+
)
253+
254+
Window(
255+
onCloseRequest = { fnConnectWindowRequest = null },
256+
state = fnConnectWindowState,
257+
title = "FN Connect 登录",
258+
icon = icon
259+
) {
260+
val desktopContext = remember(fnConnectWindowState) {
261+
val dataDir = logDir.parentFile.resolve("data").apply { if (!exists()) mkdirs() }
262+
val cacheDir = logDir.parentFile.resolve("cache").apply { if (!exists()) mkdirs() }
263+
DesktopContext(fnConnectWindowState, dataDir, cacheDir, logDir, ExtraWindowProperties())
264+
}
265+
266+
CompositionLocalProvider(
267+
LocalContext provides desktopContext,
268+
LocalPlayerManager provides remember { PlayerManager() },
269+
LocalFrameWindowScope provides this@Window,
270+
LocalWindowState provides fnConnectWindowState,
271+
LocalWindowHandle provides window.windowHandle,
272+
LocalWebViewInitialized provides (webViewInitialized && !webViewRestartRequired && webViewInitError == null),
273+
LocalWebViewRestartRequired provides webViewRestartRequired,
274+
LocalWebViewInitError provides webViewInitError
275+
) {
276+
AppTheme(
277+
displayMicaLayer = true,
278+
state = fnConnectWindowState
279+
) {
280+
FnConnectWebViewScreen(
281+
initialUrl = request.initialUrl,
282+
fnId = request.fnId,
283+
onBack = { fnConnectWindowRequest = null },
284+
onLoginSuccess = { history ->
285+
val preferencesManager = PreferencesManager.getInstance()
286+
val current = preferencesManager.loadLoginHistory()
287+
val updated = upsertLoginHistory(current, history)
288+
preferencesManager.saveLoginHistory(updated)
289+
fnConnectWindowRequest = null
290+
},
291+
autoLoginUsername = request.autoLoginUsername,
292+
autoLoginPassword = request.autoLoginPassword,
293+
allowAutoLogin = request.allowAutoLogin,
294+
draggableArea = { content -> WindowDraggableArea(content = content) }
295+
)
296+
}
297+
}
298+
}
299+
}
237300
}
238301
}
239302
}

0 commit comments

Comments
 (0)