Skip to content

Commit

Permalink
(feat) File viewing (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
wingio committed Nov 13, 2023
1 parent d452611 commit 348a1f5
Show file tree
Hide file tree
Showing 40 changed files with 1,636 additions and 14 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,3 @@
fragment RawMarkdownFile on MarkdownFileType {
contentRaw
}
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,18 @@
query RawMarkdown(
$owner: String!
$name: String!
$branch: String!
$path: String!
) {
repository(owner: $owner, name: $name) {
gitObject: object(expression: $branch) {
... on Commit {
file(path: $path) {
fileType {
...RawMarkdownFile
}
}
}
}
}
}
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,14 @@ 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 getRawMarkdown(owner: String, name: String, branch: String, path: String) =
service.getRawMarkdown(owner, name, branch, path)
.transform { it.repository?.gitObject?.onCommit?.file?.fileType?.rawMarkdownFile }

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 @@ -17,9 +17,11 @@ import com.materiiapps.gloom.gql.FollowingQuery
import com.materiiapps.gloom.gql.IdentifyQuery
import com.materiiapps.gloom.gql.JoinedOrgsQuery
import com.materiiapps.gloom.gql.ProfileQuery
import com.materiiapps.gloom.gql.RawMarkdownQuery
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 +224,20 @@ 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 getRawMarkdown(owner: String, name: String, branch: String, path: String) =
withContext(Dispatchers.IO) {
client.query(RawMarkdownQuery(owner, name, branch, path))
.addToken()
.response()
}

suspend fun getDefaultBranch(owner: String, name: String) = withContext(Dispatchers.IO) {
client.query(DefaultBranchQuery(owner, name))
.addToken()
Expand Down
5 changes: 4 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ apollo-normalized-cache = { group = "com.apollographql.apollo3", name = "apollo-
apollo-runtime = { group = "com.apollographql.apollo3", name = "apollo-runtime", version.ref = "apollo" }
coil = { group = "io.coil-kt", name = "coil", version.ref = "coil" }
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
compose-imageloader = { group = "io.github.qdsfdhvh", name = "image-loader", version = "1.6.4" }
compose-material3 = { group = "androidx.compose.material3", name = "material3", version = "1.2.0-alpha07" }
compose-material = { group = "androidx.compose.material", name = "material", version.ref = "compose" }
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "compose" }
compose-pdf = { group = "dev.zt64", name = "compose-pdf", version = "1.0.0" }
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 All @@ -51,6 +53,7 @@ voyager-koin = { group = "cafe.adriel.voyager", name = "voyager-koin", version.r
voyager-navigator = { group = "cafe.adriel.voyager", name = "voyager-navigator", version.ref = "voyager" }
voyager-tab-navigator = { group = "cafe.adriel.voyager", name = "voyager-tab-navigator", version.ref = "voyager" }
voyager-transitions = { group = "cafe.adriel.voyager", name = "voyager-transitions", version.ref = "voyager" }
zoomable = { group = "net.engawapg.lib", name = "zoomable", version = "1.5.1" }

[plugins]
aboutlibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutlibraries"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.os.Handler
import android.os.Looper
import androidx.core.content.getSystemService
import androidx.core.net.toUri
import com.benasher44.uuid.uuid4
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand All @@ -32,7 +33,7 @@ actual class DownloadManager(
).resolve("Gloom")

actual fun download(url: String, block: (String) -> Unit) {
val name = url.toUri().lastPathSegment!!
val name = url.toUri().lastPathSegment ?: "${uuid4()}.blob"
downloadScope.launch {
download(url, File(gloomDownloadFolder, name)).also {
Handler(Looper.getMainLooper()).post {
Expand Down
3 changes: 3 additions & 0 deletions ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ kotlin {

api(libs.androidx.paging.compose)
api(libs.compose.imageloader)
api(libs.compose.pdf)
api(libs.highlights)
api(libs.koin.core)
api(libs.koin.androidx.compose)
api(libs.moko.resources.compose)
api(libs.multiplatform.paging)
api(libs.zoomable)
}
}
val androidMain by getting {
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
@@ -0,0 +1,44 @@
package com.materiiapps.gloom.ui.components

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.outlined.CheckCircleOutline
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.benasher44.uuid.uuid4
import com.materiiapps.gloom.Res
import com.materiiapps.gloom.domain.manager.DownloadManager
import com.materiiapps.gloom.ui.widgets.alerts.LocalAlertController
import dev.icerock.moko.resources.compose.stringResource
import org.koin.androidx.compose.get

@Composable
fun DownloadButton(
downloadUrl: String,
modifier: Modifier = Modifier,
fileName: String = downloadUrl.split("/").lastOrNull() ?: "${uuid4()}.blob",
onDownloadFinished: (String) -> Unit = {}
) {
val downloadManager: DownloadManager = get()
val alertController = LocalAlertController.current
val downloadingText = stringResource(Res.strings.msg_downloading_file, fileName)
val downloadedText = stringResource(Res.strings.msg_download_completed)

IconButton(
onClick = {
alertController.showText(downloadingText, icon = Icons.Filled.Download)
downloadManager.download(downloadUrl) {
alertController.showText(downloadedText, icon = Icons.Outlined.CheckCircleOutline)
onDownloadFinished(it)
}
},
modifier = modifier
) {
Icon(
imageVector = Icons.Filled.Download,
contentDescription = stringResource(Res.strings.action_download)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.materiiapps.gloom.ui.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.materiiapps.gloom.Res
import dev.icerock.moko.resources.compose.stringResource

@Composable
fun ErrorMessage(
message: String,
modifier: Modifier = Modifier,
onRetryClick: (() -> Unit)? = null
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.padding(16.dp)
) {
Icon(
imageVector = Icons.Filled.Cancel,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(50.dp)
)

Text(
message,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)

Spacer(modifier = Modifier.height(16.dp))

onRetryClick?.let {
FilledTonalButton(
onClick = onRetryClick,
) {
Text(stringResource(Res.strings.action_try_again))
}
}
}
}
Loading

0 comments on commit 348a1f5

Please sign in to comment.