feat: multi-photo support in feedback (up to 5 screenshots)
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 4s

iOS:
- PhotosPicker with maxSelectionCount: 5
- Horizontal scroll preview strip with individual remove buttons
- Sends "images" array instead of single "image"

Server:
- Gateway accepts both "image" (single, backwards compat) and
  "images" (array) fields
- Uploads each as separate Gitea issue attachment

Also closed Gitea issues #11, #12, #13, #14.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-03 20:34:25 -05:00
parent f17279d5b8
commit 0b74493db0
2 changed files with 82 additions and 56 deletions

View File

@@ -100,33 +100,40 @@ def handle_feedback(handler, body, user):
result = json.loads(resp.read())
issue_number = result.get("number")
# Upload image attachment if provided
image_b64 = data.get("image")
if image_b64 and issue_number:
# Upload image attachments if provided
if issue_number:
import base64
try:
img_bytes = base64.b64decode(image_b64)
boundary = "----FeedbackBoundary123"
body_parts = []
body_parts.append(f"--{boundary}\r\n".encode())
body_parts.append(b'Content-Disposition: form-data; name="attachment"; filename="screenshot.png"\r\n')
body_parts.append(b"Content-Type: image/png\r\n\r\n")
body_parts.append(img_bytes)
body_parts.append(f"\r\n--{boundary}--\r\n".encode())
multipart_body = b"".join(body_parts)
# Support single "image" or multiple "images" array
images_b64 = data.get("images", [])
single_image = data.get("image")
if single_image:
images_b64.insert(0, single_image)
img_req = urllib.request.Request(
f"{GITEA_URL}/api/v1/repos/{REPO}/issues/{issue_number}/assets",
data=multipart_body,
headers={
"Authorization": f"token {GITEA_TOKEN}",
"Content-Type": f"multipart/form-data; boundary={boundary}",
},
method="POST",
)
urllib.request.urlopen(img_req, timeout=15)
except Exception as img_err:
pass # Image upload failed but issue was created
for idx, img_b64 in enumerate(images_b64):
try:
img_bytes = base64.b64decode(img_b64)
filename = f"screenshot{'_' + str(idx + 1) if idx > 0 else ''}.png"
boundary = f"----FeedbackBoundary{idx}"
body_parts = []
body_parts.append(f"--{boundary}\r\n".encode())
body_parts.append(f'Content-Disposition: form-data; name="attachment"; filename="{filename}"\r\n'.encode())
body_parts.append(b"Content-Type: image/png\r\n\r\n")
body_parts.append(img_bytes)
body_parts.append(f"\r\n--{boundary}--\r\n".encode())
multipart_body = b"".join(body_parts)
img_req = urllib.request.Request(
f"{GITEA_URL}/api/v1/repos/{REPO}/issues/{issue_number}/assets",
data=multipart_body,
headers={
"Authorization": f"token {GITEA_TOKEN}",
"Content-Type": f"multipart/form-data; boundary={boundary}",
},
method="POST",
)
urllib.request.urlopen(img_req, timeout=15)
except Exception:
pass # Image upload failed but issue was created
handler._send_json({
"ok": True,