Add a tags field to the Android and desktop uploaders
Each photo card gains a free-text tags input, sent as a raw comma-separated `tags` form field on upload. The server normalizes it (parseTags), so the clients stay dumb — no tag parsing on-device. - Android: PickedItem.tags + setTags; threaded through UploadQueue → UploadWorker → UploadClient's multipart. - Desktop: a tags QLineEdit per PhotoItemWidget; UploadClient.upload sends it.
This commit is contained in:
1 parent
a42abfab48
commit
faa1e01c59
11 files changed
+43
-3
No files matched your search
@@ -81,6 +81,7 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
},
|
||||
onCaptionChange = viewModel::setCaption,
|
||||
onTagsChange = viewModel::setTags,
|
||||
onDateChange = viewModel::setDate,
|
||||
onRemove = viewModel::removeItem,
|
||||
onSubmit = viewModel::submit,
|
||||
|
||||
@@ -60,6 +60,7 @@ class UploadClient(
|
||||
fileName: String,
|
||||
caption: String,
|
||||
date: String,
|
||||
tags: String,
|
||||
): UploadOutcome {
|
||||
val mediaType = if (fileName.endsWith(".png", ignoreCase = true)) {
|
||||
"image/png".toMediaType()
|
||||
@@ -72,6 +73,7 @@ class UploadClient(
|
||||
.addFormDataPart("image", fileName, file.asRequestBody(mediaType))
|
||||
.addFormDataPart("caption", caption)
|
||||
.addFormDataPart("date", date)
|
||||
.addFormDataPart("tags", tags)
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
|
||||
@@ -51,6 +51,7 @@ fun PostScreen(
|
||||
state: PostUiState,
|
||||
onPickImages: () -> Unit,
|
||||
onCaptionChange: (id: String, value: String) -> Unit,
|
||||
onTagsChange: (id: String, value: String) -> Unit,
|
||||
onDateChange: (id: String, value: LocalDate) -> Unit,
|
||||
onRemove: (id: String) -> Unit,
|
||||
onSubmit: () -> Unit,
|
||||
@@ -107,6 +108,7 @@ fun PostScreen(
|
||||
ItemCard(
|
||||
item = item,
|
||||
onCaptionChange = { onCaptionChange(item.id, it) },
|
||||
onTagsChange = { onTagsChange(item.id, it) },
|
||||
onEditDate = { dateEditFor = item },
|
||||
onRemove = { onRemove(item.id) },
|
||||
onOpenUrl = onOpenUrl,
|
||||
@@ -185,6 +187,7 @@ fun PostScreen(
|
||||
private fun ItemCard(
|
||||
item: PickedItem,
|
||||
onCaptionChange: (String) -> Unit,
|
||||
onTagsChange: (String) -> Unit,
|
||||
onEditDate: () -> Unit,
|
||||
onRemove: () -> Unit,
|
||||
onOpenUrl: (String) -> Unit,
|
||||
@@ -220,6 +223,16 @@ private fun ItemCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Spacer(Modifier.height(6.dp))
|
||||
OutlinedTextField(
|
||||
value = item.tags,
|
||||
onValueChange = onTagsChange,
|
||||
label = { Text("Tags") },
|
||||
placeholder = { Text("comma-separated") },
|
||||
singleLine = true,
|
||||
enabled = item.status !is ItemStatus.Uploading,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedButton(
|
||||
onClick = onEditDate,
|
||||
|
||||
@@ -41,6 +41,7 @@ data class PickedItem(
|
||||
val source: Uri,
|
||||
val preview: Bitmap? = null,
|
||||
val caption: String = "",
|
||||
val tags: String = "", // raw comma-separated; the server normalizes
|
||||
val date: LocalDate = LocalDate.now(),
|
||||
val status: ItemStatus = ItemStatus.Pending,
|
||||
)
|
||||
@@ -114,6 +115,10 @@ class UploadViewModel(app: Application) : AndroidViewModel(app) {
|
||||
updateItem(id) { it.copy(caption = value) }
|
||||
}
|
||||
|
||||
fun setTags(id: String, value: String) {
|
||||
updateItem(id) { it.copy(tags = value) }
|
||||
}
|
||||
|
||||
fun setDate(id: String, value: LocalDate) {
|
||||
updateItem(id) { it.copy(date = value) }
|
||||
}
|
||||
@@ -159,6 +164,7 @@ class UploadViewModel(app: Application) : AndroidViewModel(app) {
|
||||
uri = Uri.fromFile(staged),
|
||||
caption = current.caption,
|
||||
date = current.date,
|
||||
tags = current.tags,
|
||||
uniqueSuffix = item.id.take(6),
|
||||
)
|
||||
observe(item.id, workId)
|
||||
|
||||
@@ -34,6 +34,7 @@ object UploadQueue {
|
||||
uri: Uri,
|
||||
caption: String,
|
||||
date: LocalDate,
|
||||
tags: String,
|
||||
uniqueSuffix: String,
|
||||
): UUID {
|
||||
val slug = LocalDateTime.now().format(SLUG_FORMAT) + "-" + uniqueSuffix
|
||||
@@ -45,6 +46,7 @@ object UploadQueue {
|
||||
.putString(UploadWorker.KEY_SLUG, slug)
|
||||
.putString(UploadWorker.KEY_CAPTION, caption)
|
||||
.putString(UploadWorker.KEY_DATE, date.format(ISO_DATE))
|
||||
.putString(UploadWorker.KEY_TAGS, tags)
|
||||
.build(),
|
||||
)
|
||||
.setConstraints(
|
||||
|
||||
@@ -38,6 +38,7 @@ class UploadWorker(
|
||||
val slug = inputData.getString(KEY_SLUG) ?: return@withContext Result.failure()
|
||||
val caption = inputData.getString(KEY_CAPTION).orEmpty()
|
||||
val date = inputData.getString(KEY_DATE).orEmpty()
|
||||
val tags = inputData.getString(KEY_TAGS).orEmpty()
|
||||
|
||||
val settings = SettingsStore(applicationContext)
|
||||
if (!settings.isConfigured) {
|
||||
@@ -66,6 +67,7 @@ class UploadWorker(
|
||||
fileName = prepared.file.name,
|
||||
caption = caption,
|
||||
date = date,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
// Keep the staged and prepared copies only while a retry might still
|
||||
@@ -147,6 +149,7 @@ class UploadWorker(
|
||||
const val KEY_SLUG = "slug"
|
||||
const val KEY_CAPTION = "caption"
|
||||
const val KEY_DATE = "date"
|
||||
const val KEY_TAGS = "tags"
|
||||
|
||||
const val KEY_RESULT_SLUG = "result_slug"
|
||||
const val KEY_RESULT_URL = "result_url"
|
||||
|
||||
Reference in new issue
Block a user