Fix upload rollback and update app icon
Upload rollback fix: after uploading a file, do not store the remote mtime/etag from the upload response PROPFIND. Nextcloud and other WebDAV servers can change a file's Last-Modified or ETag after upload (thumbnail generation, checksums, folder aggregation). Storing stale metadata caused the next sync to see remoteChanged=true and download the file back, reverting the upload. Leaving remoteAfterTransfer=null forces the SKIP reconciliation pass to fill in remote state from the directory listing, which is the same source all future syncs use. Icon: update foreground to thick ribbons with 3D highlight stripes (blue/green/red/orange, width 12 + highlight 5); update background to dark space theme with star dots. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -107,11 +107,10 @@ class SyncEngine @Inject constructor(
|
||||
|
||||
when (decision) {
|
||||
SyncDecision.UPLOAD -> {
|
||||
var uploadedRemoteFile: RemoteFile? = null
|
||||
val bytes = runCatching {
|
||||
ensureRemoteDirs(provider, pair.remotePath, rel)
|
||||
accessor.openInputStream(rel)?.use { stream ->
|
||||
uploadedRemoteFile = provider.uploadFile(stream, "${pair.remotePath}/$rel", local!!.sizeBytes) { }.getOrThrow()
|
||||
provider.uploadFile(stream, "${pair.remotePath}/$rel", local!!.sizeBytes) { }.getOrThrow()
|
||||
}
|
||||
local!!.sizeBytes
|
||||
}.getOrElse { e ->
|
||||
@@ -120,8 +119,12 @@ class SyncEngine @Inject constructor(
|
||||
return@withPermit FileOutcome(failed = 1)
|
||||
}
|
||||
logEvent(pair.id, SyncEventType.FILE_UPLOADED, rel, null, bytes)
|
||||
// Don't store remote metadata from upload response — the server (Nextcloud etc.)
|
||||
// may change mtime/etag during post-upload processing. Leaving remoteModifiedAt
|
||||
// null forces the SKIP reconciliation on the next sync to fill it in from the
|
||||
// directory listing, which is the same source all future syncs will use.
|
||||
FileOutcome(uploaded = 1, bytesTransferred = bytes,
|
||||
newState = buildState(pair.id, rel, local!!, remoteAfterTransfer = uploadedRemoteFile))
|
||||
newState = buildState(pair.id, rel, local!!, remoteAfterTransfer = null))
|
||||
}
|
||||
SyncDecision.DOWNLOAD -> {
|
||||
val bytes = runCatching {
|
||||
|
||||
@@ -5,8 +5,41 @@
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<!-- Pure black background -->
|
||||
<path android:pathData="M0,0 H108 V108 H0 Z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- Dark space background -->
|
||||
<path
|
||||
android:pathData="M0,0 H108 V108 H0 Z"
|
||||
android:fillColor="#050F05"/>
|
||||
|
||||
<!-- Subtle dark green center glow -->
|
||||
<path
|
||||
android:pathData="M54,54 A40,40 0 1,0 54.01,54 Z"
|
||||
android:fillColor="#0D1F0D"
|
||||
android:fillAlpha="0.9"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.9"
|
||||
android:pathData="M18,12 A1.2,1.2 0 1,0 18.01,12 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.7"
|
||||
android:pathData="M88,18 A0.9,0.9 0 1,0 88.01,18 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.8"
|
||||
android:pathData="M12,55 A1.0,1.0 0 1,0 12.01,55 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.6"
|
||||
android:pathData="M95,40 A0.8,0.8 0 1,0 95.01,40 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.9"
|
||||
android:pathData="M25,90 A1.1,1.1 0 1,0 25.01,90 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.7"
|
||||
android:pathData="M82,88 A0.9,0.9 0 1,0 82.01,88 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.5"
|
||||
android:pathData="M96,72 A0.8,0.8 0 1,0 96.01,72 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.8"
|
||||
android:pathData="M8,80 A1.0,1.0 0 1,0 8.01,80 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.6"
|
||||
android:pathData="M70,8 A0.9,0.9 0 1,0 70.01,8 Z"/>
|
||||
<path android:fillColor="#FFFFFF" android:fillAlpha="0.7"
|
||||
android:pathData="M40,100 A0.8,0.8 0 1,0 40.01,100 Z"/>
|
||||
<path android:fillColor="#AAFFAA" android:fillAlpha="0.5"
|
||||
android:pathData="M92,94 A1.0,1.0 0 1,0 92.01,94 Z"/>
|
||||
<path android:fillColor="#AAAAFF" android:fillAlpha="0.4"
|
||||
android:pathData="M5,25 A0.8,0.8 0 1,0 5.01,25 Z"/>
|
||||
|
||||
</vector>
|
||||
|
||||
@@ -6,77 +6,110 @@
|
||||
android:viewportHeight="108">
|
||||
|
||||
<!--
|
||||
Four thick arcs arranged as an interlocked pinwheel.
|
||||
Each arc sweeps ~210 degrees, rounded caps, radius 18 from center (54,54).
|
||||
Draw order creates natural over/under at the four crossing points:
|
||||
blue under green, green under red, red under orange, orange under blue (re-draw blue tip).
|
||||
Four thick ribbons in an interlocked pinwheel knot.
|
||||
Each ribbon sweeps 210 degrees clockwise on a radius-18 circle centered at (54,54).
|
||||
Each ribbon is drawn as: base (width 12) + highlight stripe (width 5).
|
||||
Over/under order: Blue under Green, Green under Red, Red under Orange, Orange under Blue tip.
|
||||
|
||||
Arc endpoints computed at radius 18, sweep 210 deg clockwise:
|
||||
start angle end angle start point end point
|
||||
270 (top) 120 (54, 36) (45, 70)
|
||||
0 (right) 210 (72, 54) (39, 45)
|
||||
90 (bot) 300 (54, 72) (63, 38)
|
||||
180 (left) 390=30 (36, 54) (69, 63)
|
||||
Arc start/end points (radius 18 from center 54,54):
|
||||
Blue: start 270deg (54,36) end 120deg (45,70)
|
||||
Green: start 90deg (54,72) end 300deg (63,38)
|
||||
Red: start 0deg (72,54) end 210deg (39,45)
|
||||
Orange: start 180deg (36,54) end 30deg (69,63)
|
||||
-->
|
||||
|
||||
<!-- Blue — starts at top, sweeps clockwise to lower-left -->
|
||||
<!-- Blue ribbon base (goes under Green start and Orange end) -->
|
||||
<path
|
||||
android:strokeColor="#2979FF"
|
||||
android:strokeWidth="8.5"
|
||||
android:strokeColor="#1565C0"
|
||||
android:strokeWidth="12"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 54,36 A 18,18 0 1,1 45,70"/>
|
||||
<!-- Blue ribbon highlight stripe -->
|
||||
<path
|
||||
android:strokeColor="#90CAF9"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 54,36 A 18,18 0 1,1 45,70"/>
|
||||
|
||||
<!-- Green — starts at bottom, sweeps clockwise to upper-right -->
|
||||
<!-- Green ribbon base (over Blue start, under Red end) -->
|
||||
<path
|
||||
android:strokeColor="#00C853"
|
||||
android:strokeWidth="8.5"
|
||||
android:strokeColor="#2E7D32"
|
||||
android:strokeWidth="12"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 54,72 A 18,18 0 1,1 63,38"/>
|
||||
<!-- Green ribbon highlight stripe -->
|
||||
<path
|
||||
android:strokeColor="#A5D6A7"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 54,72 A 18,18 0 1,1 63,38"/>
|
||||
|
||||
<!-- Red — starts at right, sweeps clockwise to lower-left -->
|
||||
<!-- Red ribbon base (over Green start, under Orange end) -->
|
||||
<path
|
||||
android:strokeColor="#E53935"
|
||||
android:strokeWidth="8.5"
|
||||
android:strokeColor="#C62828"
|
||||
android:strokeWidth="12"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 72,54 A 18,18 0 1,1 39,45"/>
|
||||
<!-- Red ribbon highlight stripe -->
|
||||
<path
|
||||
android:strokeColor="#EF9A9A"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 72,54 A 18,18 0 1,1 39,45"/>
|
||||
|
||||
<!-- Orange — starts at left, sweeps clockwise to upper-right -->
|
||||
<!-- Orange ribbon base (over Red start, under Blue tip) -->
|
||||
<path
|
||||
android:strokeColor="#FF6D00"
|
||||
android:strokeWidth="8.5"
|
||||
android:strokeColor="#E65100"
|
||||
android:strokeWidth="12"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 36,54 A 18,18 0 1,1 69,63"/>
|
||||
<!-- Orange ribbon highlight stripe -->
|
||||
<path
|
||||
android:strokeColor="#FFCC80"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 36,54 A 18,18 0 1,1 69,63"/>
|
||||
|
||||
<!-- Re-draw blue start cap on top so it goes OVER orange end -->
|
||||
<!-- Redraw Blue start cap on top so it goes OVER Orange end -->
|
||||
<path
|
||||
android:strokeColor="#2979FF"
|
||||
android:strokeWidth="8.5"
|
||||
android:strokeColor="#1565C0"
|
||||
android:strokeWidth="12"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 54,36 A 18,18 0 0,1 62,37.5"/>
|
||||
<path
|
||||
android:strokeColor="#90CAF9"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M 54,36 A 18,18 0 0,1 62,37.5"/>
|
||||
|
||||
<!-- White sync circle at center -->
|
||||
<!-- Black circle behind center sync icon -->
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 45,54 A 9,9 0 1,0 63,54 A 9,9 0 1,0 45,54 Z"/>
|
||||
|
||||
<!-- Sync ring -->
|
||||
<!-- White sync ring -->
|
||||
<path
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeWidth="2.5"
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M 46.5,54 A 7.5,7.5 0 1,0 61.5,54 A 7.5,7.5 0 1,0 46.5,54 Z"/>
|
||||
|
||||
<!-- Top arrow head (pointing up) -->
|
||||
<!-- Up arrow (pointing up) -->
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M 54,46.5 L 57,50.5 L 51,50.5 Z"/>
|
||||
|
||||
<!-- Bottom arrow head (pointing down) -->
|
||||
<!-- Down arrow (pointing down) -->
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M 54,61.5 L 51,57.5 L 57,57.5 Z"/>
|
||||
|
||||
Reference in New Issue
Block a user