Skip to content

Commit

Permalink
Add code and markdown file viewing
Browse files Browse the repository at this point in the history
  • Loading branch information
wingio committed Oct 2, 2023
1 parent c064f64 commit ae20b7d
Show file tree
Hide file tree
Showing 28 changed files with 588 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fragment NodeId on Node {
id
__typename
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
fragment RepoFile on Repository {
id
gitObject: object(expression: $branch) {
__typename
...NodeId
... on Commit {
file(path: $path) {
extension
path
fileType {
__typename
... on MarkdownFileType {
contentHTML
}
... on ImageFileType {
url
}
... on PdfFileType {
url
}
... on TextFileType {
contentRaw
}
}
}
id
}
}
viewerCanPush
ref(qualifiedName: $branch) {
id
viewerCanCommitToBranch
target {
id
oid
}
__typename
}
__typename
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
query RepoFile(
$owner: String!
$name: String!
$branch: String!
$path: String!
) {
repository(owner: $owner, name: $name) {
__typename
...RepoFile
id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ class GraphQLRepository(
?: emptyList()
}

suspend fun getRepoFile(owner: String, name: String, branch: String, path: String) =
service.getRepoFile(owner, name, branch, path)
.transform { it.repository?.repoFile }

suspend fun getDefaultBranch(owner: String, name: String) =
service.getDefaultBranch(owner, name).transform {
it.repository?.defaultBranchRef?.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.materiiapps.gloom.gql.ProfileQuery
import com.materiiapps.gloom.gql.ReactMutation
import com.materiiapps.gloom.gql.ReleaseDetailsQuery
import com.materiiapps.gloom.gql.RepoDetailsQuery
import com.materiiapps.gloom.gql.RepoFileQuery
import com.materiiapps.gloom.gql.RepoFilesQuery
import com.materiiapps.gloom.gql.RepoIssuesQuery
import com.materiiapps.gloom.gql.RepoListQuery
Expand Down Expand Up @@ -222,6 +223,13 @@ class GraphQLService(
.response()
}

suspend fun getRepoFile(owner: String, name: String, branch: String, path: String) =
withContext(Dispatchers.IO) {
client.query(RepoFileQuery(owner, name, branch, path))
.addToken()
.response()
}

suspend fun getDefaultBranch(owner: String, name: String) = withContext(Dispatchers.IO) {
client.query(DefaultBranchQuery(owner, name))
.addToken()
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ compose-material = { group = "androidx.compose.material", name = "material", ver
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "compose" }
compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose" }
compose-imageloader = { group = "io.github.qdsfdhvh", name = "image-loader", version = "1.6.4" }
highlights = { group = "dev.snipme", name = "highlights", version = "0.7.1" }
koin-androidx-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version = "3.4.0" }
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" }
Expand Down
1 change: 1 addition & 0 deletions ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ kotlin {

api(libs.androidx.paging.compose)
api(libs.compose.imageloader)
api(libs.highlights)
api(libs.koin.core)
api(libs.koin.androidx.compose)
api(libs.moko.resources.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.materiiapps.gloom.di

import com.materiiapps.gloom.ui.viewmodels.auth.LandingViewModel
import com.materiiapps.gloom.ui.viewmodels.explorer.DirectoryListingViewModel
import com.materiiapps.gloom.ui.viewmodels.explorer.FileViewerViewModel
import com.materiiapps.gloom.ui.viewmodels.home.HomeViewModel
import com.materiiapps.gloom.ui.viewmodels.list.OrgListViewModel
import com.materiiapps.gloom.ui.viewmodels.list.RepositoryListViewModel
Expand Down Expand Up @@ -42,6 +43,7 @@ fun viewModelModule() = module {
factoryOf(::RepoDetailsViewModel)
factoryOf(::RepoCodeViewModel)
factoryOf(::DirectoryListingViewModel)
factoryOf(::FileViewerViewModel)
factoryOf(::RepoIssuesViewModel)
factoryOf(::RepoPullRequestsViewModel)
factoryOf(::RepoReleasesViewModel)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.materiiapps.gloom.ui.components

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
import com.materiiapps.gloom.ui.utils.fromExtension
import dev.snipme.highlights.Highlights
import dev.snipme.highlights.model.BoldHighlight
import dev.snipme.highlights.model.ColorHighlight
import dev.snipme.highlights.model.SyntaxLanguage
import dev.snipme.highlights.model.SyntaxTheme

@Composable
fun CodeText(
text: String,
extension: String,
theme: SyntaxTheme,
softWrap: Boolean = false,
fontSize: TextUnit = 13.sp,
modifier: Modifier = Modifier
) {
val hl = remember(text, theme) {
Highlights
.default()
.getBuilder()
.code(text)
.language(SyntaxLanguage.fromExtension(extension))
.theme(theme)
.build()
}
val highlights = remember(hl) { hl.getHighlights() }

Text(
text = buildAnnotatedString {
append(hl.getCode())

if(!(extension.isBlank() || extension == ".txt")) {
highlights
.filterIsInstance<ColorHighlight>()
.forEach {
addStyle(
SpanStyle(color = Color(it.rgb).copy(alpha = 1f)),
start = it.location.start,
end = it.location.end
)
}

highlights
.filterIsInstance<BoldHighlight>()
.forEach {
addStyle(
SpanStyle(fontWeight = FontWeight.Bold),
start = it.location.start,
end = it.location.end
)
}
}
},
fontFamily = FontFamily.Monospace,
softWrap = softWrap,
fontSize = fontSize,
modifier = modifier
)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.materiiapps.gloom.ui.components
package com.materiiapps.gloom.ui.components.toolbar

import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import com.materiiapps.gloom.ui.components.BackButton

@Composable
@OptIn(ExperimentalMaterial3Api::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.materiiapps.gloom.ui.components.toolbar

import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import com.materiiapps.gloom.ui.components.BackButton

@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun SmallToolbar(
title: String,
actions: @Composable RowScope.() -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null
) {
TopAppBar(
title = { Text(text = title) },
navigationIcon = { BackButton() },
actions = actions,
scrollBehavior = scrollBehavior
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import cafe.adriel.voyager.koin.getScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import com.benasher44.uuid.uuid4
import com.materiiapps.gloom.ui.utils.navigate
import com.materiiapps.gloom.ui.viewmodels.explorer.DirectoryListingViewModel
import org.koin.core.parameter.parametersOf

Expand Down Expand Up @@ -61,14 +62,27 @@ class DirectoryListingScreen(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable {
if (entry.type == "tree")
nav.push(
when(entry.type) {
"tree" -> nav.push(
DirectoryListingScreen(
owner,
name,
branchAndPath + "${entry.name}/"
)
)
"blob" -> {
val (branch, path) = branchAndPath.split(":")
nav.navigate(
FileViewerScreen(
owner,
name,
branch,
"$path${entry.name}"
)
)
}
}

}
.padding(22.dp)
.fillMaxWidth()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.materiiapps.gloom.ui.screens.explorer

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.koin.getScreenModel
import com.materiiapps.gloom.gql.fragment.RepoFile
import com.materiiapps.gloom.ui.components.RefreshIndicator
import com.materiiapps.gloom.ui.components.toolbar.SmallToolbar
import com.materiiapps.gloom.ui.screens.explorer.viewers.MarkdownFileViewer
import com.materiiapps.gloom.ui.screens.explorer.viewers.TextFileViewer
import com.materiiapps.gloom.ui.viewmodels.explorer.FileViewerViewModel
import org.koin.core.parameter.parametersOf

class FileViewerScreen(
private val owner: String,
private val name: String,
private val branch: String,
private val path: String
): Screen {

@Composable
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
override fun Content() {
val viewModel: FileViewerViewModel = getScreenModel { parametersOf(FileViewerViewModel.Input(owner, name, branch, path)) }
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val pullRefreshState = rememberPullRefreshState(viewModel.isLoading, onRefresh = { viewModel.getRepoFile() })
val file = viewModel.file?.gitObject?.onCommit?.file

Scaffold(
topBar = { Toolbar(scrollBehavior, file) },
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { pv ->
Box(
modifier = Modifier
.padding(pv)
.fillMaxSize()
.pullRefresh(pullRefreshState)
) {
when(viewModel.hasError) {
true -> {
Icon(
Icons.Filled.Cancel,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.align(Alignment.Center)
)
}

false -> FileContent(file)
}

RefreshIndicator(pullRefreshState, viewModel.isLoading)
}
}
}

@Composable
private fun FileContent(file: RepoFile.File?) {
when(file?.fileType?.__typename) {
"MarkdownFileType" -> MarkdownFileViewer(file.fileType?.onMarkdownFileType!!)
"ImageFileType" -> Box(Modifier.fillMaxSize())
"PdfFileType" -> Box(Modifier.fillMaxSize())
"TextFileType" -> TextFileViewer(file.fileType?.onTextFileType!!, file.extension ?: "")
else -> {}
}
}

@Composable
private fun RowScope.FileActions(file: RepoFile.File?) {
// TODO: Different actions for each file type
}

@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun Toolbar(
scrollBehavior: TopAppBarScrollBehavior,
file: RepoFile.File?
) {
SmallToolbar(
title = path.split("/").lastOrNull() ?: "File",
actions = { FileActions(file) },
scrollBehavior = scrollBehavior
)
}

}
Loading

0 comments on commit ae20b7d

Please sign in to comment.