Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
68fc757
feat: add native upload mediaUpload override for local HTTP server pi…
dcalhoun Mar 6, 2026
f9dacb0
feat(ios): add local HTTP upload server and media upload delegate
dcalhoun Mar 6, 2026
32d65b7
feat(android): add local HTTP upload server and media upload delegate
dcalhoun Mar 6, 2026
9330f3b
refactor: make uploadFile optional on MediaUploadDelegate
dcalhoun Mar 6, 2026
05c3304
feat: add image resize delegate to iOS and Android demo apps
dcalhoun Mar 6, 2026
e6ddd96
test: add MediaUploadServer and delegate tests for iOS
dcalhoun Mar 6, 2026
4a4bf30
fix(ios): treat NWListener .waiting as transient instead of terminal
dcalhoun Mar 6, 2026
b5f039e
fix(ios): set NWListener connection handler before start
dcalhoun Mar 6, 2026
894957a
fix(ios): fix multipart parser and use raw sockets in upload tests
dcalhoun Mar 6, 2026
ce2f0b9
fix: route media uploads through native server via apiFetch middleware
dcalhoun Mar 6, 2026
7aa1cca
task: set Android demo app log levels to debug
dcalhoun Mar 8, 2026
5d6b207
fix: defer upload server start until delegate is set
dcalhoun Mar 8, 2026
39d85e5
refactor: remove unnecessary Access-Control-Allow-Private-Network header
dcalhoun Mar 8, 2026
525fe24
test(android): add MediaUploadServer and delegate tests
dcalhoun Mar 8, 2026
e0a5a31
test: add nativeMediaUploadMiddleware tests
dcalhoun Mar 8, 2026
826ffa4
fix(ios): skip upload server when no media upload delegate is set
dcalhoun Mar 8, 2026
d1f1e36
feat: add native media upload toggle to iOS and Android demo apps
dcalhoun Mar 8, 2026
58b4101
fix: improve error reporting for unexpected WordPress upload responses
dcalhoun Mar 9, 2026
c33fd49
fix: prevent temp file name collisions during concurrent uploads
dcalhoun Mar 9, 2026
a889fc3
fix(android): reuse OkHttpClient instance for upload server
dcalhoun Mar 9, 2026
9c49808
fix(android): stop previous upload server on delegate re-assignment
dcalhoun Mar 9, 2026
46181fc
fix(android): use JSONObject for upload response serialization
dcalhoun Mar 9, 2026
d177fe7
docs(ios): clarify serverRef race window in MediaUploadServer init
dcalhoun Mar 9, 2026
9e62275
fix(ios): remove emojis from log messages
dcalhoun Mar 9, 2026
621fbf8
fix: transform native upload response to WP REST API shape
dcalhoun Mar 9, 2026
2555c92
fix: add 250 MB upload size limit to local media servers
dcalhoun Mar 9, 2026
d593dc6
fix: clean up temp files after media upload processing
dcalhoun Mar 9, 2026
1045103
fix(android): replace response.body!! with safe null check
dcalhoun Mar 9, 2026
f2a91c1
fix: add Connection: close header to local server responses
dcalhoun Mar 9, 2026
9ce8fb8
fix(js): throw Error instance instead of plain object on upload failure
dcalhoun Mar 9, 2026
1dc228d
docs: clarify upload size limit scope and nonisolated(unsafe) safety
dcalhoun Mar 9, 2026
2a21262
refactor: remove unused native-upload module
dcalhoun Mar 9, 2026
63e020f
fix(js): tighten media upload path matching to exact endpoint
dcalhoun Mar 9, 2026
6bc2a0e
docs: document memory buffering and runBlocking trade-offs
dcalhoun Mar 9, 2026
7115d8b
fix(android): revert logLevel default from "debug" to "warn"
dcalhoun Mar 9, 2026
29a2b41
fix(ios): use consistent Logger subsystem and remove unused error case
dcalhoun Mar 9, 2026
2517872
perf(ios): cache parsed headers in isRequestComplete
dcalhoun Mar 9, 2026
edb28cf
fix(android): return 413 instead of 400 for oversized uploads
dcalhoun Mar 9, 2026
890107b
refactor(ios): move Logger.uploadServer to EditorLogging.swift
dcalhoun Mar 9, 2026
fe9cb4b
fix: align log tag and media path matching for consistency
dcalhoun Mar 9, 2026
3f0c320
fix(ios): return 413 for oversized uploads instead of 400
dcalhoun Mar 9, 2026
51a88b5
fix(js): show user-friendly error for oversized upload (413)
dcalhoun Mar 9, 2026
a1807ec
fix: sanitize uploaded filenames to prevent path traversal
dcalhoun Mar 9, 2026
64d64cb
fix(android): add org.json test dependency to fix unit test failures
dcalhoun Mar 9, 2026
55ec5de
refactor: Revert unnecessary test mocks
dcalhoun Mar 9, 2026
0637225
refactor(ios): remove unused chunked transfer-encoding support from u…
dcalhoun Mar 9, 2026
02fb9bf
refactor(js): extract shared media endpoint regex constant
dcalhoun Mar 9, 2026
f73694f
fix(ios): use non-failable UTF-8 conversion in Data.append helper
dcalhoun Mar 9, 2026
241b848
refactor(ios): replace POSIX socket test helper with URLSession
dcalhoun Mar 9, 2026
a97f86a
fix(ios): check HTTP status code in DefaultMediaUploader
dcalhoun Mar 9, 2026
337ad29
feat: include media_details in native upload response
dcalhoun Mar 9, 2026
e03a425
style(ios): use 4-space indentation in media upload files
dcalhoun Mar 9, 2026
8f9a061
refactor: use structured logging in demo media upload delegates
dcalhoun Mar 9, 2026
5dc5aaa
refactor(ios): use structured logging in upload server startup
dcalhoun Mar 9, 2026
15f4274
refactor(android): extract DemoMediaUploadDelegate to its own file
dcalhoun Mar 9, 2026
a150aa1
style: Reinstate line between imports and class declaration
dcalhoun Mar 9, 2026
4cd2cac
test: remove superfluous protocol default and Codable tests
dcalhoun Mar 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/Gutenberg/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ dependencies {
implementation(libs.jsoup)
implementation(libs.okhttp)

testImplementation(libs.json)
testImplementation(libs.junit)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.mockito.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,24 @@ class GutenbergView : WebView {

var requestInterceptor: GutenbergRequestInterceptor = DefaultGutenbergRequestInterceptor()

/** Optional delegate for customizing media upload behavior (resize, transcode, custom upload). */
var mediaUploadDelegate: MediaUploadDelegate? = null
set(value) {
field = value
// Stop any previously running server before starting a new one.
uploadServer?.stop()
uploadServer = null
// (Re)start the upload server so it captures the delegate.
// This handles the common case where the delegate is set after
// construction but before the editor finishes loading.
if (value != null) {
startUploadServer()
}
}

private var uploadServer: MediaUploadServer? = null
private val uploadHttpClient: okhttp3.OkHttpClient by lazy { okhttp3.OkHttpClient() }

private var onFileChooserRequested: ((Intent, Int) -> Unit)? = null
private var contentChangeListener: ContentChangeListener? = null
private var historyChangeListener: HistoryChangeListener? = null
Expand Down Expand Up @@ -432,7 +450,12 @@ class GutenbergView : WebView {
}

private fun setGlobalJavaScriptVariables() {
val gbKit = GBKitGlobal.fromConfiguration(configuration, dependencies)
val gbKit = GBKitGlobal.fromConfiguration(
configuration,
dependencies,
nativeUploadPort = uploadServer?.port,
nativeUploadToken = uploadServer?.token
)
val gbKitJson = gbKit.toJsonString()
val gbKitConfig = """
window.GBKit = $gbKitJson;
Expand All @@ -443,6 +466,24 @@ class GutenbergView : WebView {
}


private fun startUploadServer() {
if (configuration.siteApiRoot.isEmpty() || configuration.authHeader.isEmpty()) return

try {
val defaultUploader = DefaultMediaUploader(
httpClient = uploadHttpClient,
siteApiRoot = configuration.siteApiRoot,
authHeader = configuration.authHeader
)
uploadServer = MediaUploadServer(
uploadDelegate = mediaUploadDelegate,
defaultUploader = defaultUploader
)
} catch (e: Exception) {
Log.w(TAG, "Failed to start upload server", e)
}
}

fun clearConfig() {
val jsCode = """
delete window.GBKit;
Expand Down Expand Up @@ -861,6 +902,8 @@ class GutenbergView : WebView {

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
uploadServer?.stop()
uploadServer = null
clearConfig()
this.stopLoading()
FileCache.clearCache(context)
Expand All @@ -881,6 +924,8 @@ class GutenbergView : WebView {
}

companion object {
private const val TAG = "GutenbergView"

/** Hosts that are safe to serve assets over HTTP (local development only). */
private val LOCAL_HOSTS = setOf("localhost", "127.0.0.1", "10.0.2.2")

Expand Down
Loading
Loading