summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/ui/runtime/WasmDistributionRuntime.kt11
-rw-r--r--modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebApp.kt33
-rw-r--r--modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebConfig.kt53
-rw-r--r--modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebHome.kt18
4 files changed, 104 insertions, 11 deletions
diff --git a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/ui/runtime/WasmDistributionRuntime.kt b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/ui/runtime/WasmDistributionRuntime.kt
index 5452b94..ceec974 100644
--- a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/ui/runtime/WasmDistributionRuntime.kt
+++ b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/ui/runtime/WasmDistributionRuntime.kt
@@ -47,7 +47,7 @@ import kotlinx.serialization.json.Json
class WasmDistributionRuntime(
private val selfPeerId: String = "wasm-${Clock.System.now().toEpochMilliseconds() % 100000}",
private var signalingEndpoint: String = OrgFlowAppStore.DEFAULT_SIGNALING_ENDPOINT,
- private val roomId: String = "default",
+ private var roomId: String = "default",
private var iceServers: List<String> = OrgFlowAppStore.DEFAULT_ICE_SERVERS,
) : DistributionRuntime, ConfigRuntime, SlideOpTransport {
@@ -177,6 +177,15 @@ class WasmDistributionRuntime(
refreshSnapshot()
}
+ fun setRoom(newRoomId: String) {
+ val next = newRoomId.trim().ifEmpty { "default" }
+ if (next == roomId) return
+ roomId = next
+ lastEvent = "room switched to $next"
+ refreshSnapshot()
+ if (started) restart()
+ }
+
fun localVideoSink(): RtcVideoSink? = rtcPlatform?.localVideoSink()
override fun sendChat(gid: String, body: String, targetPeerIds: List<String>, threadId: String) {
diff --git a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebApp.kt b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebApp.kt
index 67d3c5e..b571fe4 100644
--- a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebApp.kt
+++ b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebApp.kt
@@ -2,6 +2,7 @@
package jp.orgflow.web
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -57,6 +58,7 @@ enum class WebRoute(val label: String) {
NOTE("Note"),
CALENDAR("Calendar"),
LIVE("Live"),
+ CONFIG("Config"),
}
class WebNavState {
@@ -72,6 +74,7 @@ fun WebApp(runtime: OrgFlowRuntime = remember { provideRuntime() }) {
val persistence = remember { StorePersistenceController(store) }
val scope = androidx.compose.runtime.rememberCoroutineScope()
val wasm = runtime.distribution as? jp.orgflow.ui.runtime.WasmDistributionRuntime
+ val filter = remember { jp.orgflow.ui.model.SetFilterState() }
LaunchedEffect(runtime, store) {
val autoEndpoint = webAutoEndpoint(OrgFlowAppStore.DEFAULT_SIGNALING_ENDPOINT)
@@ -113,19 +116,30 @@ fun WebApp(runtime: OrgFlowRuntime = remember { provideRuntime() }) {
}
}
+ LaunchedEffect(store.joinedRoomIds) {
+ wasm?.setRoom(store.joinedRoomIds.lastOrNull() ?: "default")
+ }
+
WebTheme {
androidx.compose.material.Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
- Row(modifier = Modifier.fillMaxSize()) {
- WebNavRail(nav, modifier = Modifier.fillMaxHeight().width(120.dp))
- Column(modifier = Modifier.weight(1f).fillMaxHeight().padding(12.dp)) {
- when (nav.current) {
- WebRoute.HOME -> WebHomeScreen(store, runtime)
- WebRoute.CREATE -> WebCreateScreen(store)
- WebRoute.NOTE -> WebNoteScreen(store)
- WebRoute.CALENDAR -> WebCalendarScreen(store)
- WebRoute.LIVE -> WebLiveScreen(store, runtime)
+ androidx.compose.foundation.layout.Box(modifier = Modifier.fillMaxSize()) {
+ Row(modifier = Modifier.fillMaxSize()) {
+ WebNavRail(nav, modifier = Modifier.fillMaxHeight().width(120.dp))
+ Column(modifier = Modifier.weight(1f).fillMaxHeight().padding(12.dp)) {
+ when (nav.current) {
+ WebRoute.HOME -> WebHomeScreen(store, runtime)
+ WebRoute.CREATE -> WebCreateScreen(store)
+ WebRoute.NOTE -> WebNoteScreen(store)
+ WebRoute.CALENDAR -> WebCalendarScreen(store)
+ WebRoute.LIVE -> WebLiveScreen(store, runtime)
+ WebRoute.CONFIG -> WebConfigScreen(runtime)
+ }
}
}
+ jp.orgflow.ui.pie.PieMenu(
+ filter = filter,
+ modifier = Modifier.align(Alignment.BottomEnd).padding(12.dp),
+ )
}
}
}
@@ -168,6 +182,7 @@ private fun WebNavRail(nav: WebNavState, modifier: Modifier = Modifier) {
color = if (selected) MaterialTheme.colors.primary.copy(alpha = 0.16f) else Color.Transparent,
shape = RoundedCornerShape(8.dp),
)
+ .clickable { nav.current = route }
.padding(horizontal = 12.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
diff --git a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebConfig.kt b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebConfig.kt
new file mode 100644
index 0000000..95ecc8b
--- /dev/null
+++ b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebConfig.kt
@@ -0,0 +1,53 @@
+package jp.orgflow.web
+
+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 jp.orgflow.ui.runtime.OrgFlowRuntime
+import jp.orgflow.ui.runtime.WasmDistributionRuntime
+
+@Composable
+fun WebConfigScreen(runtime: OrgFlowRuntime) {
+ val config by runtime.config.config.collectAsState()
+ val snapshot by runtime.distribution.snapshot.collectAsState()
+ val wasm = runtime.distribution as? WasmDistributionRuntime
+ var endpoint by remember(config.signalingEndpoint) { mutableStateOf(config.signalingEndpoint) }
+ var ice by remember(config.iceServers) { mutableStateOf(config.iceServers.joinToString(", ")) }
+ var message by remember { mutableStateOf<String?>(null) }
+
+ WebScrollColumn {
+ WebSection("接続設定") {
+ WebTextField(endpoint, { endpoint = it }, "シグナリング URL (ws://… または wss://…)")
+ WebActionButton("シグナリングを適用") {
+ if (endpoint.trim().isNotBlank()) {
+ wasm?.updateEndpoint(endpoint.trim())
+ message = "シグナリングを更新しました"
+ }
+ }
+ WebTextField(ice, { ice = it }, "ICE サーバ (カンマ区切り)")
+ WebActionButton("ICE サーバを適用") {
+ val servers = ice.split(",").map { it.trim() }.filter { it.isNotBlank() }
+ if (servers.isNotEmpty()) {
+ wasm?.updateIceServers(servers)
+ message = "ICE サーバを更新しました (再接続後に有効)"
+ }
+ }
+ message?.let { WebStatusLine(it) }
+ }
+ WebSection("現在の状態") {
+ WebStatusLine("ピアID: ${snapshot.selfPeerId}")
+ WebStatusLine("シグナリング: ${snapshot.signalingEndpoint}")
+ WebStatusLine("接続: ${if (snapshot.connected) "OK" else "未接続"} / エンジン: ${if (snapshot.engineReady) "OK" else "待機中"}")
+ WebStatusLine("ピア数: ${snapshot.peers.size}")
+ WebStatusLine("最新イベント: ${snapshot.lastEvent}")
+ }
+ WebSection("ヒント") {
+ WebStatusLine("カメラ/画面共有は https または localhost でのみ使用できます")
+ WebStatusLine("他のピアとつながらない場合は同じシグナリング URL・同じルームID か確認してください")
+ WebStatusLine("wss を使う場合は ops/HTTPS-SETUP.md (lighttpd設定) を参照してください")
+ }
+ }
+}
diff --git a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebHome.kt b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebHome.kt
index d2de2d6..52b4721 100644
--- a/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebHome.kt
+++ b/modules/orgflow-ui/src/wasmJsMain/kotlin/jp/orgflow/web/WebHome.kt
@@ -71,9 +71,14 @@ fun WebHomeScreen(store: OrgFlowAppStore, runtime: OrgFlowRuntime) {
@Composable
private fun WebRoomsSection(store: OrgFlowAppStore) {
var showRegister by remember { mutableStateOf(false) }
+ var quickId by remember { mutableStateOf("ws-classroom") }
WebSection("ルーム") {
if (store.joinableRooms.isEmpty()) {
- WebStatusLine("ルームがありません (ルームを作成して共有しましょう)")
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ listOf("ws-classroom" to "Classroom", "ws-club" to "Club", "ws-committee" to "Committee").forEach { (id, name) ->
+ WebActionButton(name, primary = false) { store.registerRoom(id, name) }
+ }
+ }
} else {
store.joinableRooms.forEach { room ->
val joined = room.id in store.joinedRoomIds
@@ -87,6 +92,17 @@ private fun WebRoomsSection(store: OrgFlowAppStore) {
}
}
}
+ Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ WebTextField(quickId, { quickId = it }, "ルームIDを入力して参加", modifier = Modifier.weight(1f))
+ WebActionButton("参加", enabled = quickId.trim().isNotBlank()) {
+ val id = quickId.trim()
+ if (store.joinableRooms.none { it.id == id }) {
+ store.registerRoom(id, id)
+ } else {
+ store.joinRoom(id)
+ }
+ }
+ }
WebActionButton(if (showRegister) "閉じる" else "ルームを作る", primary = false) { showRegister = !showRegister }
if (showRegister) {
RegisterRoomForm(store)