diff --git a/app/src/main/java/chat/revolt/activities/MainActivity.kt b/app/src/main/java/chat/revolt/activities/MainActivity.kt index ca6c8b8b..e6e90371 100644 --- a/app/src/main/java/chat/revolt/activities/MainActivity.kt +++ b/app/src/main/java/chat/revolt/activities/MainActivity.kt @@ -40,6 +40,7 @@ import chat.revolt.screens.register.OnboardingScreen import chat.revolt.screens.register.RegisterDetailsScreen import chat.revolt.screens.register.RegisterGreetingScreen import chat.revolt.screens.register.RegisterVerifyScreen +import chat.revolt.screens.services.DiscoverScreen import chat.revolt.screens.settings.AppearanceSettingsScreen import chat.revolt.screens.settings.ChangelogsSettingsScreen import chat.revolt.screens.settings.ClosedBetaUpdaterScreen @@ -145,6 +146,8 @@ fun AppEntrypoint(windowSizeClass: WindowSizeClass) { composable("chat") { ChatRouterScreen(navController, windowSizeClass) } + composable("discover") { DiscoverScreen(navController) } + composable("settings") { SettingsScreen(navController) } composable("settings/profile") { ProfileSettingsScreen(navController) } composable("settings/sessions") { SessionSettingsScreen(navController) } diff --git a/app/src/main/java/chat/revolt/api/internals/ThemeCompat.kt b/app/src/main/java/chat/revolt/api/internals/ThemeCompat.kt new file mode 100644 index 00000000..06107cff --- /dev/null +++ b/app/src/main/java/chat/revolt/api/internals/ThemeCompat.kt @@ -0,0 +1,109 @@ +package chat.revolt.api.internals + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive + +fun Color.asComponentJsonPrimitiveString(): JsonPrimitive { + val rgb = this.toArgb() + val r = rgb shr 16 and 0xFF + val g = rgb shr 8 and 0xFF + val b = rgb shr 0 and 0xFF + return JsonPrimitive("$r, $g, $b") +} + +fun Color.asHexJsonPrimitiveString(): JsonPrimitive { + val rgb = this.toArgb() + val r = rgb shr 16 and 0xFF + val g = rgb shr 8 and 0xFF + val b = rgb shr 0 and 0xFF + return JsonPrimitive("#${r.toString(16)}${g.toString(16)}${b.toString(16)}") +} + +object ThemeCompat { + @Composable + fun materialThemeAsDiscoverTheme(materialTheme: MaterialTheme): JsonObject { + // https://github.com/revoltchat/discover/blob/6effdf4a611e89b38b5e6bccefa1cd999e4b545f/styles/variables.scss + return JsonObject( + mapOf( + "accent" to materialTheme.colorScheme.primary.asHexJsonPrimitiveString(), + "accent-rgb" to materialTheme.colorScheme.primary.asComponentJsonPrimitiveString(), + "background" to materialTheme.colorScheme.background.asHexJsonPrimitiveString(), + "background-rgb" to materialTheme.colorScheme.background.asComponentJsonPrimitiveString(), + "foreground" to materialTheme.colorScheme.onBackground.asHexJsonPrimitiveString(), + "foreground-rgb" to materialTheme.colorScheme.onBackground.asComponentJsonPrimitiveString(), + "block" to materialTheme.colorScheme.surface.asHexJsonPrimitiveString(), + "block-rgb" to materialTheme.colorScheme.surface.asComponentJsonPrimitiveString(), + "message-box" to materialTheme.colorScheme.surface.asHexJsonPrimitiveString(), + "message-box-rgb" to materialTheme.colorScheme.surface.asComponentJsonPrimitiveString(), + "mention" to materialTheme.colorScheme.primary.copy(alpha = 0.1f) + .asHexJsonPrimitiveString(), + "mention-rgb" to materialTheme.colorScheme.primary.copy(alpha = 0.1f) + .asComponentJsonPrimitiveString(), + "success" to materialTheme.colorScheme.primary.asHexJsonPrimitiveString(), + "success-rgb" to materialTheme.colorScheme.primary.asComponentJsonPrimitiveString(), + "warning" to materialTheme.colorScheme.secondary.asHexJsonPrimitiveString(), + "warning-rgb" to materialTheme.colorScheme.secondary.asComponentJsonPrimitiveString(), + "error" to materialTheme.colorScheme.error.asHexJsonPrimitiveString(), + "error-rgb" to materialTheme.colorScheme.error.asComponentJsonPrimitiveString(), + "hover" to materialTheme.colorScheme.primary.copy(alpha = 0.1f) + .asHexJsonPrimitiveString(), + "hover-rgb" to materialTheme.colorScheme.primary.copy(alpha = 0.1f) + .asComponentJsonPrimitiveString(), + "scrollbar-thumb" to materialTheme.colorScheme.primary.asHexJsonPrimitiveString(), + "scrollbar-thumb-rgb" to materialTheme.colorScheme.primary.asComponentJsonPrimitiveString(), + "scrollbar-track" to materialTheme.colorScheme.background.asHexJsonPrimitiveString(), + "scrollbar-track-rgb" to materialTheme.colorScheme.background.asComponentJsonPrimitiveString(), + "primary-background" to materialTheme.colorScheme.background.asHexJsonPrimitiveString(), + "primary-background-rgb" to materialTheme.colorScheme.background.asComponentJsonPrimitiveString(), + "primary-header" to materialTheme.colorScheme.background.asHexJsonPrimitiveString(), + "primary-header-rgb" to materialTheme.colorScheme.background.asComponentJsonPrimitiveString(), + "secondary-background" to materialTheme.colorScheme.surface.asHexJsonPrimitiveString(), + "secondary-background-rgb" to materialTheme.colorScheme.surface.asComponentJsonPrimitiveString(), + "secondary-foreground" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "secondary-foreground-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "secondary-header" to materialTheme.colorScheme.surface.asHexJsonPrimitiveString(), + "secondary-header-rgb" to materialTheme.colorScheme.surface.asComponentJsonPrimitiveString(), + "tertiary-background" to materialTheme.colorScheme.surface.asHexJsonPrimitiveString(), + "tertiary-background-rgb" to materialTheme.colorScheme.surface.asComponentJsonPrimitiveString(), + "tertiary-foreground" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "tertiary-foreground-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "accent-contrast" to materialTheme.colorScheme.onPrimary.asHexJsonPrimitiveString(), + "accent-contrast-rgb" to materialTheme.colorScheme.onPrimary.asComponentJsonPrimitiveString(), + "background-contrast" to materialTheme.colorScheme.onBackground.asHexJsonPrimitiveString(), + "background-contrast-rgb" to materialTheme.colorScheme.onBackground.asComponentJsonPrimitiveString(), + "foreground-contrast" to materialTheme.colorScheme.inverseOnSurface.asHexJsonPrimitiveString(), + "foreground-contrast-rgb" to materialTheme.colorScheme.inverseOnSurface.asComponentJsonPrimitiveString(), + "block-contrast" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "block-contrast-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "message-box-contrast" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "message-box-contrast-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "mention-contrast" to materialTheme.colorScheme.onPrimary.asHexJsonPrimitiveString(), + "mention-contrast-rgb" to materialTheme.colorScheme.onPrimary.asComponentJsonPrimitiveString(), + "success-contrast" to materialTheme.colorScheme.onPrimary.asHexJsonPrimitiveString(), + "success-contrast-rgb" to materialTheme.colorScheme.onPrimary.asComponentJsonPrimitiveString(), + "warning-contrast" to materialTheme.colorScheme.onSecondary.asHexJsonPrimitiveString(), + "warning-contrast-rgb" to materialTheme.colorScheme.onSecondary.asComponentJsonPrimitiveString(), + "error-contrast" to materialTheme.colorScheme.onError.asHexJsonPrimitiveString(), + "error-contrast-rgb" to materialTheme.colorScheme.onError.asComponentJsonPrimitiveString(), + "primary-background-contrast" to materialTheme.colorScheme.onBackground.asHexJsonPrimitiveString(), + "primary-background-contrast-rgb" to materialTheme.colorScheme.onBackground.asComponentJsonPrimitiveString(), + "primary-header-contrast" to materialTheme.colorScheme.onBackground.asHexJsonPrimitiveString(), + "primary-header-contrast-rgb" to materialTheme.colorScheme.onBackground.asComponentJsonPrimitiveString(), + "secondary-background-contrast" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "secondary-background-contrast-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "secondary-foreground-contrast" to materialTheme.colorScheme.inverseOnSurface.asHexJsonPrimitiveString(), + "secondary-foreground-contrast-rgb" to materialTheme.colorScheme.inverseOnSurface.asComponentJsonPrimitiveString(), + "secondary-header-contrast" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "secondary-header-contrast-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "tertiary-background-contrast" to materialTheme.colorScheme.onSurface.asHexJsonPrimitiveString(), + "tertiary-background-contrast-rgb" to materialTheme.colorScheme.onSurface.asComponentJsonPrimitiveString(), + "tertiary-foreground-contrast" to materialTheme.colorScheme.inverseOnSurface.asHexJsonPrimitiveString(), + "tertiary-foreground-contrast-rgb" to materialTheme.colorScheme.inverseOnSurface.asComponentJsonPrimitiveString(), + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/screens/services/DiscoverView.kt b/app/src/main/java/chat/revolt/components/screens/services/DiscoverView.kt new file mode 100644 index 00000000..99b493ec --- /dev/null +++ b/app/src/main/java/chat/revolt/components/screens/services/DiscoverView.kt @@ -0,0 +1,138 @@ +package chat.revolt.components.screens.services + +import android.annotation.SuppressLint +import android.content.Intent +import android.net.Uri +import android.webkit.WebResourceRequest +import android.webkit.WebView +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import chat.revolt.activities.InviteActivity +import chat.revolt.api.REVOLT_APP +import chat.revolt.api.RevoltJson +import chat.revolt.api.buildUserAgent +import chat.revolt.api.internals.ThemeCompat +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive + +@SuppressLint("SetJavaScriptEnabled") +@Composable +fun ColumnScope.DiscoverView() { + var showPlaceholder by remember { mutableStateOf(true) } + val animatedAlpha by animateFloatAsState( + targetValue = if (showPlaceholder) 1f else 0f, + label = "discoverViewPlaceholderAlpha" + ) + val themeMap = ThemeCompat.materialThemeAsDiscoverTheme(MaterialTheme) + + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .weight(1f) + ) { + AndroidView( + factory = { context -> + WebView(context).apply { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + settings.userAgentString = buildUserAgent("DiscoverView") + settings.setSupportZoom(false) + settings.setSupportMultipleWindows(false) + loadUrl("https://rvlt.gg/discover/servers?embedded=true") + + webViewClient = object : android.webkit.WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + val themeMessage = JsonObject( + mapOf( + "source" to JsonPrimitive("revolt"), + "type" to JsonPrimitive("theme"), + "theme" to themeMap + ) + ) + val themeMessageString = + RevoltJson.encodeToString(JsonObject.serializer(), themeMessage) + + evaluateJavascript( + """ + window.postMessage($themeMessageString, "*") + window.addEventListener("message", event => { + try { + const data = JSON.parse(event.data) + if (data.source === "discover") { + switch (data.type) { + case "navigate": + // Cheap debounce + if (Date.now() - window.lastNavigateEvent < 500) { + return + } + window.lastNavigateEvent = Date.now() + window.location.href = data.url + break + } + } + } catch(e) {} + }) + """.trimIndent(), + null + ) + + postDelayed({ + showPlaceholder = false + }, 1000) // to prevent flickering + } + + override fun shouldOverrideUrlLoading( + view: WebView?, + request: WebResourceRequest? + ): Boolean { + if (request?.url?.host.equals(Uri.parse(REVOLT_APP).host)) { + val intent = Intent( + context, + InviteActivity::class.java + ).setAction(Intent.ACTION_VIEW) + + intent.data = request?.url + context.startActivity(intent) + + return true + } + + if (!request?.url?.host.equals("rvlt.gg")) { + return true + } + + return false + } + } + } + }, + update = { + }, + modifier = Modifier + .fillMaxSize() + .alpha(1f - animatedAlpha) + ) + + CircularProgressIndicator( + modifier = Modifier + .size(48.dp) + .alpha(animatedAlpha) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt index d2c40988..d40ea521 100644 --- a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt @@ -700,6 +700,7 @@ fun ChatRouterScreen( ) { Sidebar( viewModel = viewModel, + topNav = topNav, navController = navController, onShowStatusSheet = { showStatusSheet = true @@ -736,6 +737,7 @@ fun ChatRouterScreen( ) { Sidebar( viewModel = viewModel, + topNav = topNav, navController = navController, onShowStatusSheet = { showStatusSheet = true @@ -777,6 +779,7 @@ fun ChatRouterScreen( @Composable fun Sidebar( viewModel: ChatRouterViewModel, + topNav: NavController, navController: NavHostController, drawerState: DrawerState? = null, onShowStatusSheet: () -> Unit, @@ -919,8 +922,6 @@ fun Sidebar( server.id ), onLongClick = { - /*serverContextSheetTarget = server.id - showServerContextSheet = true*/ onShowServerContextSheet(server.id) } ) { @@ -940,6 +941,16 @@ fun Sidebar( modifier = Modifier.padding(4.dp) ) } + + DrawerServerlikeIcon( + onClick = { topNav.navigate("discover") } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_compass_24dp), + contentDescription = stringResource(id = R.string.discover_alt), + modifier = Modifier.padding(4.dp) + ) + } } Crossfade( diff --git a/app/src/main/java/chat/revolt/screens/services/DiscoverScreen.kt b/app/src/main/java/chat/revolt/screens/services/DiscoverScreen.kt new file mode 100644 index 00000000..65254205 --- /dev/null +++ b/app/src/main/java/chat/revolt/screens/services/DiscoverScreen.kt @@ -0,0 +1,30 @@ +package chat.revolt.screens.services + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.navigation.NavController +import chat.revolt.R +import chat.revolt.components.generic.PageHeader +import chat.revolt.components.screens.services.DiscoverView + +@Composable +fun DiscoverScreen(navController: NavController) { + Column( + Modifier + .fillMaxSize() + .safeDrawingPadding() + ) { + PageHeader( + text = stringResource(R.string.discover), + showBackButton = true, + onBackButtonClicked = { + navController.popBackStack() + } + ) + DiscoverView() + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_compass_24dp.xml b/app/src/main/res/drawable/ic_compass_24dp.xml new file mode 100644 index 00000000..fe8eb884 --- /dev/null +++ b/app/src/main/res/drawable/ic_compass_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f32f0381..09e9386e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -130,6 +130,7 @@ Clear all incoming requests Add server + Discover Bit awkward. There aren\'t any channels in this server. Not even a welcome channel. How rude. @@ -300,6 +301,8 @@ Create a new server This feature is currently under construction. + Discover Revolt + Report Cancel