mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 12:25:22 +00:00
Improve camera UX: Direct camera access with camera icon
- Change paperclip icon to camera icon for clearer affordance - Open camera directly on tap (no confirmation dialog) - Add CameraPickerView wrapper for UIImagePickerController - Remove PhotosPicker in favor of direct camera access - Images still processed through ImageUtils with EXIF stripping - Accessibility: Added 'Take photo' label
This commit is contained in:
+539
-392
@@ -182,8 +182,188 @@
|
|||||||
},
|
},
|
||||||
"shouldTranslate" : false
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
|
"%@ active" : {
|
||||||
|
"comment" : "A label at the bottom of the people list sheet showing the number of active users.",
|
||||||
|
"localizations" : {
|
||||||
|
"ar" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ نشطين"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bn" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ সক্রিয়"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktiv"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ active"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ activos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fil" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktibo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ actifs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"he" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ פעילים"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hi" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ सक्रिय"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktif"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"it" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ attivi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ 人がアクティブ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ko" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "활성 사용자 %@명"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktif"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ne" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ सक्रिय"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ actief"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktywni"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ ativos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt-BR" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ ativos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ru" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ активных"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sv" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktiva"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ta" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ செயலில்"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"th" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ กำลังใช้งาน"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ aktif"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uk" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ активних"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ur" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ فعال"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vi" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%@ đang hoạt động"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "活跃用户 %@ 名"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "活躍使用者 %@ 位"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"%@/%@" : {
|
"%@/%@" : {
|
||||||
"comment" : "A label showing the number of messages that have been successfully delivered, followed by a slash and the total number of messages. Both numbers are displayed in monospaced font.",
|
"comment" : "A label showing the number of messages that have been successfully delivered, followed by a slash and the total number of messages. Both numbers are displayed in monospaced font.",
|
||||||
|
"extractionState" : "stale",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"ar" : {
|
"ar" : {
|
||||||
@@ -362,40 +542,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"%@ active": {
|
|
||||||
"comment": "A label at the bottom of the people list sheet showing the number of active users.",
|
|
||||||
"localizations": {
|
|
||||||
"ar": { "stringUnit": { "state": "translated", "value": "%@ نشطين" } },
|
|
||||||
"bn": { "stringUnit": { "state": "translated", "value": "%@ সক্রিয়" } },
|
|
||||||
"de": { "stringUnit": { "state": "translated", "value": "%@ aktiv" } },
|
|
||||||
"en": { "stringUnit": { "state": "translated", "value": "%@ active" } },
|
|
||||||
"es": { "stringUnit": { "state": "translated", "value": "%@ activos" } },
|
|
||||||
"fil": { "stringUnit": { "state": "translated", "value": "%@ aktibo" } },
|
|
||||||
"fr": { "stringUnit": { "state": "translated", "value": "%@ actifs" } },
|
|
||||||
"he": { "stringUnit": { "state": "translated", "value": "%@ פעילים" } },
|
|
||||||
"hi": { "stringUnit": { "state": "translated", "value": "%@ सक्रिय" } },
|
|
||||||
"id": { "stringUnit": { "state": "translated", "value": "%@ aktif" } },
|
|
||||||
"it": { "stringUnit": { "state": "translated", "value": "%@ attivi" } },
|
|
||||||
"ja": { "stringUnit": { "state": "translated", "value": "%@ 人がアクティブ" } },
|
|
||||||
"ko": { "stringUnit": { "state": "translated", "value": "활성 사용자 %@명" } },
|
|
||||||
"ms": { "stringUnit": { "state": "translated", "value": "%@ aktif" } },
|
|
||||||
"ne": { "stringUnit": { "state": "translated", "value": "%@ सक्रिय" } },
|
|
||||||
"nl": { "stringUnit": { "state": "translated", "value": "%@ actief" } },
|
|
||||||
"pl": { "stringUnit": { "state": "translated", "value": "%@ aktywni" } },
|
|
||||||
"pt": { "stringUnit": { "state": "translated", "value": "%@ ativos" } },
|
|
||||||
"pt-BR": { "stringUnit": { "state": "translated", "value": "%@ ativos" } },
|
|
||||||
"ru": { "stringUnit": { "state": "translated", "value": "%@ активных" } },
|
|
||||||
"sv": { "stringUnit": { "state": "translated", "value": "%@ aktiva" } },
|
|
||||||
"ta": { "stringUnit": { "state": "translated", "value": "%@ செயலில்" } },
|
|
||||||
"th": { "stringUnit": { "state": "translated", "value": "%@ กำลังใช้งาน" } },
|
|
||||||
"tr": { "stringUnit": { "state": "translated", "value": "%@ aktif" } },
|
|
||||||
"uk": { "stringUnit": { "state": "translated", "value": "%@ активних" } },
|
|
||||||
"ur": { "stringUnit": { "state": "translated", "value": "%@ فعال" } },
|
|
||||||
"vi": { "stringUnit": { "state": "translated", "value": "%@ đang hoạt động" } },
|
|
||||||
"zh-Hans": { "stringUnit": { "state": "translated", "value": "活跃用户 %@ 名" } },
|
|
||||||
"zh-Hant": { "stringUnit": { "state": "translated", "value": "活躍使用者 %@ 位" } }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app_info.app_name" : {
|
"app_info.app_name" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -30905,6 +31051,365 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"open" : {
|
||||||
|
"comment" : "Button to open attached file",
|
||||||
|
"extractionState" : "stale",
|
||||||
|
"localizations" : {
|
||||||
|
"ar" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "فتح"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bn" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "খুলুন"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "öffnen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "open"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "abrir"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fil" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "buksan"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "ouvrir"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"he" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "פתח"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hi" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "खोलें"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "buka"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"it" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "apri"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "開く"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ko" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "열기"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "buka"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ne" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "खोल्नुहोस्"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "openen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "otwórz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "abrir"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt-BR" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "abrir"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ru" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "открыть"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sv" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "öppna"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ta" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "திற"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"th" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "เปิด"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "aç"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uk" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "відкрити"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ur" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "کھولیں"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vi" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "mở"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "打开"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "開啟"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recording %@" : {
|
||||||
|
"comment" : "Voice note recording duration indicator",
|
||||||
|
"localizations" : {
|
||||||
|
"ar" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "التسجيل %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bn" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "রেকর্ডিং %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "aufnahme %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "recording %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "grabando %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fil" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "nagrerekord %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "enregistrement %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"he" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "מקליט %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hi" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "रिकॉर्डिंग %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "merekam %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"it" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "registrazione %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "録音中 %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ko" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "녹음 중 %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "merakam %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ne" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "रेकर्डिङ %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "opname %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "nagrywanie %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "gravando %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt-BR" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "gravando %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ru" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "запись %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sv" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "spelar in %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ta" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "பதிவு செய்கிறது %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"th" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "กำลังบันทึก %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "kaydediliyor %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uk" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "запис %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ur" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "ریکارڈنگ %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vi" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "đang ghi %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "录音中 %@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "錄音中 %@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"save" : {
|
"save" : {
|
||||||
"comment" : "Button to save media to device",
|
"comment" : "Button to save media to device",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
@@ -35559,364 +36064,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"open": {
|
|
||||||
"comment": "Button to open attached file",
|
|
||||||
"localizations": {
|
|
||||||
"ar": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "فتح"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bn": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "খুলুন"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"de": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "öffnen"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "open"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "abrir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fil": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "buksan"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fr": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "ouvrir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"he": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "פתח"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hi": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "खोलें"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "buka"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"it": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "apri"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ja": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "開く"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ko": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "열기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "buka"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ne": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "खोल्नुहोस्"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nl": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "openen"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pl": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "otwórz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pt": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "abrir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pt-BR": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "abrir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ru": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "открыть"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sv": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "öppna"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ta": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "திற"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"th": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "เปิด"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tr": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "aç"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"uk": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "відкрити"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ur": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "کھولیں"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vi": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "mở"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zh-Hans": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "打开"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zh-Hant": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "開啟"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recording %@": {
|
|
||||||
"comment": "Voice note recording duration indicator",
|
|
||||||
"localizations": {
|
|
||||||
"ar": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "التسجيل %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bn": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "রেকর্ডিং %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"de": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "aufnahme %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "recording %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "grabando %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fil": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "nagrerekord %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fr": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "enregistrement %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"he": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "מקליט %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hi": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "रिकॉर्डिंग %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "merekam %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"it": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "registrazione %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ja": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "録音中 %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ko": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "녹음 중 %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "merakam %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ne": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "रेकर्डिङ %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nl": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "opname %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pl": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "nagrywanie %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pt": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "gravando %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pt-BR": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "gravando %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ru": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "запись %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sv": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "spelar in %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ta": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "பதிவு செய்கிறது %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"th": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "กำลังบันทึก %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tr": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "kaydediliyor %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"uk": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "запис %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ur": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "ریکارڈنگ %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vi": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "đang ghi %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zh-Hans": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "录音中 %@"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zh-Hant": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "錄音中 %@"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version" : "1.1"
|
"version" : "1.1"
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ import AppKit
|
|||||||
#endif
|
#endif
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
import BitLogger
|
import BitLogger
|
||||||
#if canImport(PhotosUI)
|
|
||||||
import PhotosUI
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - Supporting Types
|
// MARK: - Supporting Types
|
||||||
|
|
||||||
@@ -72,10 +69,8 @@ struct ContentView: View {
|
|||||||
@State private var recordingTimer: Timer?
|
@State private var recordingTimer: Timer?
|
||||||
@State private var recordingStartDate: Date?
|
@State private var recordingStartDate: Date?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@State private var showPhotoPicker = false
|
@State private var showCameraPicker = false
|
||||||
@State private var selectedPhotoPickerItem: PhotosPickerItem?
|
|
||||||
#endif
|
#endif
|
||||||
@State private var showAttachmentActions = false
|
|
||||||
@ScaledMetric(relativeTo: .body) private var headerHeight: CGFloat = 44
|
@ScaledMetric(relativeTo: .body) private var headerHeight: CGFloat = 44
|
||||||
@ScaledMetric(relativeTo: .subheadline) private var headerPeerIconSize: CGFloat = 11
|
@ScaledMetric(relativeTo: .subheadline) private var headerPeerIconSize: CGFloat = 11
|
||||||
@ScaledMetric(relativeTo: .subheadline) private var headerPeerCountFontSize: CGFloat = 12
|
@ScaledMetric(relativeTo: .subheadline) private var headerPeerCountFontSize: CGFloat = 12
|
||||||
@@ -209,10 +204,22 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.photosPicker(isPresented: $showPhotoPicker, selection: $selectedPhotoPickerItem, matching: .images)
|
.sheet(isPresented: $showCameraPicker) {
|
||||||
.onChange(of: selectedPhotoPickerItem) { newItem in
|
CameraPickerView { image in
|
||||||
guard let item = newItem else { return }
|
showCameraPicker = false
|
||||||
Task { await handlePhotoSelection(item) }
|
if let image = image {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let processedURL = try ImageUtils.processImage(image)
|
||||||
|
await MainActor.run {
|
||||||
|
viewModel.sendImage(from: processedURL)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
SecureLogger.error("Camera image processing failed: \(error)", category: .session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
.sheet(isPresented: Binding(
|
.sheet(isPresented: Binding(
|
||||||
@@ -223,15 +230,6 @@ struct ContentView: View {
|
|||||||
ImagePreviewView(url: url)
|
ImagePreviewView(url: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.confirmationDialog("Attach", isPresented: $showAttachmentActions, titleVisibility: .visible) {
|
|
||||||
#if os(iOS)
|
|
||||||
Button("Image") {
|
|
||||||
showAttachmentActions = false
|
|
||||||
DispatchQueue.main.async { showPhotoPicker = true }
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Button("Cancel", role: .cancel) {}
|
|
||||||
}
|
|
||||||
.alert("Recording Error", isPresented: $showRecordingAlert, actions: {
|
.alert("Recording Error", isPresented: $showRecordingAlert, actions: {
|
||||||
Button("OK", role: .cancel) {}
|
Button("OK", role: .cancel) {}
|
||||||
}, message: {
|
}, message: {
|
||||||
@@ -1805,13 +1803,18 @@ private extension ContentView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var attachmentButton: some View {
|
var attachmentButton: some View {
|
||||||
Button(action: { showAttachmentActions = true }) {
|
Button(action: {
|
||||||
Image(systemName: "paperclip.circle.fill")
|
#if os(iOS)
|
||||||
|
showCameraPicker = true
|
||||||
|
#endif
|
||||||
|
}) {
|
||||||
|
Image(systemName: "camera.circle.fill")
|
||||||
.font(.bitchatSystem(size: 24))
|
.font(.bitchatSystem(size: 24))
|
||||||
.foregroundColor(composerAccentColor)
|
.foregroundColor(composerAccentColor)
|
||||||
.frame(width: 36, height: 36)
|
.frame(width: 36, height: 36)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.accessibilityLabel("Take photo")
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendOrMicButton: some View {
|
var sendOrMicButton: some View {
|
||||||
@@ -1980,26 +1983,6 @@ private extension ContentView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
func handlePhotoSelection(_ item: PhotosPickerItem) async {
|
|
||||||
defer { Task { @MainActor in selectedPhotoPickerItem = nil } }
|
|
||||||
do {
|
|
||||||
if let data = try await item.loadTransferable(type: Data.self) {
|
|
||||||
let tempURL = FileManager.default.temporaryDirectory
|
|
||||||
.appendingPathComponent(UUID().uuidString)
|
|
||||||
.appendingPathExtension("jpg")
|
|
||||||
try data.write(to: tempURL, options: Data.WritingOptions.atomic)
|
|
||||||
await MainActor.run {
|
|
||||||
viewModel.sendImage(from: tempURL) {
|
|
||||||
try? FileManager.default.removeItem(at: tempURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
SecureLogger.error("Photo picker load failed: \(error)", category: .session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
func applicationFilesDirectory() -> URL? {
|
func applicationFilesDirectory() -> URL? {
|
||||||
// Cache the directory lookup to avoid repeated FileManager calls during view rendering
|
// Cache the directory lookup to avoid repeated FileManager calls during view rendering
|
||||||
@@ -2143,3 +2126,41 @@ struct ImagePreviewView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
// MARK: - Camera Picker
|
||||||
|
struct CameraPickerView: UIViewControllerRepresentable {
|
||||||
|
let completion: (UIImage?) -> Void
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> UIImagePickerController {
|
||||||
|
let picker = UIImagePickerController()
|
||||||
|
picker.sourceType = .camera
|
||||||
|
picker.delegate = context.coordinator
|
||||||
|
picker.allowsEditing = false
|
||||||
|
return picker
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
||||||
|
let completion: (UIImage?) -> Void
|
||||||
|
|
||||||
|
init(completion: @escaping (UIImage?) -> Void) {
|
||||||
|
self.completion = completion
|
||||||
|
}
|
||||||
|
|
||||||
|
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
|
||||||
|
let image = info[.originalImage] as? UIImage
|
||||||
|
completion(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
||||||
|
completion(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user