Skip to content

Commit 73da6f4

Browse files
authored
Add Text/Code Previews (#34)
- Add Text/Code Previews - Add reAuthenticate to Upload
1 parent 4981cae commit 73da6f4

File tree

14 files changed

+291
-17
lines changed

14 files changed

+291
-17
lines changed

.github/scripts/prepare.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
# https://github.com/django-files/android-client
3+
4+
set -e
5+
6+
HLJS_VERSION="11.11.1"
7+
8+
which git || (echo "Missing: git" && exit 1)
9+
which npm || (echo "Missing: npm" && exit 1)
10+
which node || (echo "Missing: node" && exit 1)
11+
12+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13+
echo "SCRIPT_DIR: ${SCRIPT_DIR}"
14+
15+
echo "Processing highlight.js: ${HLJS_VERSION}"
16+
17+
SOURCE_DIR="$(realpath "${SCRIPT_DIR}/../highlightjs")"
18+
echo "SOURCE_DIR: ${SOURCE_DIR}"
19+
if [ -d "${SOURCE_DIR}" ];then
20+
echo "Removing: ${SOURCE_DIR}"
21+
rm -rf "${SOURCE_DIR}"
22+
fi
23+
24+
git clone https://github.com/highlightjs/highlight.js.git "${SOURCE_DIR}"
25+
cd "${SOURCE_DIR}"
26+
git checkout "${HLJS_VERSION}"
27+
28+
npm install
29+
node tools/build.js :common
30+
31+
TARGET_DIR="$(realpath "${SCRIPT_DIR}/../../app/src/main/assets/preview/dist")"
32+
echo "TARGET_DIR: ${TARGET_DIR}"
33+
mkdir -p "${TARGET_DIR}"
34+
35+
cp build/highlight.min.js "${TARGET_DIR}"
36+
cp src/styles/stackoverflow-dark.css "${TARGET_DIR}"
37+
cp src/styles/stackoverflow-light.css "${TARGET_DIR}"
38+
39+
echo "Finished: highlight.js"
40+
41+
echo "Everything is finished."

.github/workflows/release.yaml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,27 @@ jobs:
8181
cat app/build.gradle.kts
8282
echo "::endgroup::"
8383
84+
- name: "Setup Node 22"
85+
uses: actions/setup-node@v4
86+
with:
87+
node-version: 22
88+
89+
- name: "Prepare Build"
90+
working-directory: ".github/scripts"
91+
run: |
92+
bash prepare.sh
93+
8494
- name: "Setup Java"
8595
uses: actions/setup-java@v4
8696
with:
8797
distribution: "zulu"
8898
java-version: "17"
8999
#cache: "gradle"
90100

91-
- name: "Set Gradle Executable"
92-
run: chmod +x ./gradlew
93-
94101
- name: "Gradle Assemble"
95-
run: ./gradlew assembleRelease
102+
run: |
103+
chmod +x ./gradlew
104+
./gradlew assembleRelease
96105
97106
- name: "Verify Build"
98107
run: |

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ app/release
1313
local.properties
1414
*.keystore
1515
*.logcat
16+
17+
**/dist/

.prettierignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# IDE
2+
.idea/
3+
.vscode/
4+
5+
# Build
6+
**/dist/
7+
**/node_modules/
8+
9+
# App
10+
app/src/main/assets/preview/**

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ should take you to the settings area to allow installation if not already enable
7070

7171
</details>
7272

73-
> [!NOTE]
74-
> Swipe from the left to access the Android menu.
75-
7673
### Setup
7774

7875
1. [Install](#Install) and open the app on your device.
@@ -84,6 +81,9 @@ To use, share or open any file and choose the Django Files app.
8481
The app will then upload the file to your Django Files server.
8582
Additionally, the URL is copied to the clipboard and the preview show in the app.
8683

84+
> [!NOTE]
85+
> Swipe from the left to access the Android menu.
86+
8787
## Features
8888

8989
- Share or Open any file and automatically copy the URL to the clipboard.
@@ -141,6 +141,10 @@ To Build:
141141
- Select the Build Variant (debug or release)
142142
- Build > Generate App Bundles or APK > Generate APKs
143143

144+
> [!NOTE]
145+
> Text/Code Previews use highlight.js; to install this run:
146+
> `bash .github/scripts/prepare.sh`
147+
144148
## Command Line
145149

146150
_Note: This section is a WIP! For more details see the [release.yaml](.github/workflows/release.yaml)._
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
html,
2+
body,
3+
pre {
4+
margin: 0;
5+
}
6+
7+
pre {
8+
padding: 6px;
9+
}
10+
11+
html,
12+
body {
13+
background: #1c1b1b;
14+
color: #ccc;
15+
}
16+
17+
@media (prefers-color-scheme: light) {
18+
html,
19+
body {
20+
background: #f6f6f6;
21+
color: #1c1b1b;
22+
}
23+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Preview</title>
6+
7+
<link rel="stylesheet" href="preview.css">
8+
<link rel="stylesheet" href="dist/stackoverflow-dark.css" id="code-dark">
9+
<link rel="stylesheet" href="dist/stackoverflow-light.css" id="code-light" disabled>
10+
</head>
11+
<body>
12+
<pre></pre>
13+
14+
<script src="dist/highlight.min.js"></script>
15+
<script src="preview.js"></script>
16+
</body>
17+
</html>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const preEl = document.querySelector('pre')
2+
const darkStyle = document.getElementById('code-dark')
3+
const lightStyle = document.getElementById('code-light')
4+
5+
const observer = new MutationObserver(mutationObserver)
6+
observer.observe(preEl, { childList: true, subtree: true })
7+
8+
document.addEventListener('DOMContentLoaded', () => {
9+
console.log('DOMContentLoaded')
10+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
11+
applyTheme(mediaQuery)
12+
mediaQuery.addEventListener('change', applyTheme)
13+
})
14+
15+
function mutationObserver(mutationsList) {
16+
for (const mutation of mutationsList) {
17+
if (mutation.type === 'childList') {
18+
console.log('highlightElement')
19+
hljs.highlightElement(preEl)
20+
}
21+
}
22+
}
23+
24+
function applyTheme(mediaQuery) {
25+
console.log(`applyTheme: matches: ${mediaQuery.matches}`)
26+
if (mediaQuery.matches) {
27+
darkStyle.disabled = false
28+
lightStyle.disabled = true
29+
} else {
30+
darkStyle.disabled = true
31+
lightStyle.disabled = false
32+
}
33+
}

app/src/main/java/com/djangofiles/djangofiles/ServerApi.kt

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import android.content.SharedPreferences
55
import android.os.Parcelable
66
import android.util.Log
77
import android.webkit.CookieManager
8+
import androidx.core.content.edit
89
import com.google.gson.annotations.SerializedName
910
import kotlinx.coroutines.Dispatchers
1011
import kotlinx.coroutines.withContext
1112
import kotlinx.parcelize.Parcelize
1213
import okhttp3.Cookie
1314
import okhttp3.CookieJar
15+
import okhttp3.Headers
1416
import okhttp3.HttpUrl
1517
import okhttp3.HttpUrl.Companion.toHttpUrl
1618
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -35,8 +37,7 @@ import retrofit2.http.Query
3537
import java.io.InputStream
3638
import java.net.URLConnection
3739

38-
// TODO: Pass preferences instead of context since context is not used
39-
class ServerApi(context: Context, host: String) {
40+
class ServerApi(val context: Context, host: String) {
4041
val api: ApiService
4142
val hostname: String = host
4243
val authToken: String
@@ -87,6 +88,29 @@ class ServerApi(context: Context, host: String) {
8788
}
8889
}
8990

91+
private suspend fun reAuthenticate(): String? {
92+
return try {
93+
val cookies = CookieManager.getInstance().getCookie(hostname)
94+
Log.d("reAuthenticate", "cookies: $cookies")
95+
val httpUrl = hostname.toHttpUrl()
96+
cookieJar.setCookie(httpUrl, cookies)
97+
98+
val tokenResponse = api.getToken()
99+
Log.d("reAuthenticate", "tokenResponse: $tokenResponse")
100+
101+
preferences.edit { putString("auth_token", tokenResponse.token) }
102+
Log.d("reAuthenticate", "auth_token: ${tokenResponse.token}")
103+
val dao: ServerDao = ServerDatabase.getInstance(context).serverDao()
104+
withContext(Dispatchers.IO) {
105+
dao.setToken(hostname, tokenResponse.token)
106+
}
107+
tokenResponse.token
108+
} catch (e: Exception) {
109+
Log.e("reAuthenticate", "Exception: ${e.message}")
110+
null
111+
}
112+
}
113+
90114
suspend fun methods(): MethodsResponse {
91115
Log.d("Api[methods]", "getMethods")
92116
return api.getMethods()
@@ -95,7 +119,17 @@ class ServerApi(context: Context, host: String) {
95119
suspend fun upload(fileName: String, inputStream: InputStream): Response<FileResponse> {
96120
Log.d("Api[upload]", "fileName: $fileName")
97121
val multiPart: MultipartBody.Part = inputStreamToMultipart(inputStream, fileName)
98-
return api.postUpload(authToken, multiPart)
122+
var response = api.postUpload(authToken, multiPart)
123+
// TODO: Determine how to make this block a reusable function...
124+
Log.d("Api[upload]", "response.code: ${response.code()}")
125+
if (response.code() == 401) {
126+
val token = reAuthenticate()
127+
Log.d("Api[upload]", "token: $token")
128+
if (token != null) {
129+
response = api.postUpload(token, multiPart)
130+
}
131+
}
132+
return response
99133
}
100134

101135
suspend fun shorten(url: String, vanity: String?): Response<ShortResponse> {
@@ -266,9 +300,9 @@ class ServerApi(context: Context, host: String) {
266300
return cookieStore[url.host] ?: emptyList()
267301
}
268302

269-
//fun setCookie(url: HttpUrl, rawCookie: String) {
270-
// val cookies = Cookie.parseAll(url, Headers.headersOf("Set-Cookie", rawCookie))
271-
// cookieStore[url.host] = cookies
272-
//}
303+
fun setCookie(url: HttpUrl, rawCookie: String) {
304+
val cookies = Cookie.parseAll(url, Headers.headersOf("Set-Cookie", rawCookie))
305+
cookieStore[url.host] = cookies
306+
}
273307
}
274-
}
308+
}

app/src/main/java/com/djangofiles/djangofiles/ui/files/FilesBottomSheet.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class FilesBottomSheet : BottomSheetDialogFragment() {
128128
api.deleteFile(fileId)
129129
viewModel.deleteId.value = fileId
130130
withContext(Dispatchers.Main) {
131-
Toast.makeText(requireContext(), "Ralf Broke It!", Toast.LENGTH_SHORT)
131+
Toast.makeText(requireContext(), "File Deleted!", Toast.LENGTH_SHORT)
132132
.show()
133133
}
134134
dismiss()

0 commit comments

Comments
 (0)